Move the gail tests from standalone gail to gtk+. Bug #504568.
authorChristian Persch <chpe@src.gnome.org>
Fri, 28 Dec 2007 20:23:14 +0000 (20:23 +0000)
committerChristian Persch <chpe@src.gnome.org>
Fri, 28 Dec 2007 20:23:14 +0000 (20:23 +0000)
svn path=/trunk/; revision=19284

28 files changed:
ChangeLog
configure.in
modules/other/gail/Makefile.am
modules/other/gail/tests/Makefile.am [new file with mode: 0644]
modules/other/gail/tests/README [new file with mode: 0644]
modules/other/gail/tests/ferret.c [new file with mode: 0644]
modules/other/gail/tests/testaction.c [new file with mode: 0644]
modules/other/gail/tests/testbutton.c [new file with mode: 0644]
modules/other/gail/tests/testcombo.c [new file with mode: 0644]
modules/other/gail/tests/testcomponent.c [new file with mode: 0644]
modules/other/gail/tests/testimage.c [new file with mode: 0644]
modules/other/gail/tests/testlib.c [new file with mode: 0644]
modules/other/gail/tests/testlib.h [new file with mode: 0644]
modules/other/gail/tests/testmenuitem.c [new file with mode: 0644]
modules/other/gail/tests/testnotebook.c [new file with mode: 0644]
modules/other/gail/tests/testobject.c [new file with mode: 0644]
modules/other/gail/tests/testoptionmenu.c [new file with mode: 0644]
modules/other/gail/tests/testpaned.c [new file with mode: 0644]
modules/other/gail/tests/testprops.c [new file with mode: 0644]
modules/other/gail/tests/testselection.c [new file with mode: 0644]
modules/other/gail/tests/teststatusbar.c [new file with mode: 0644]
modules/other/gail/tests/testtable.c [new file with mode: 0644]
modules/other/gail/tests/testtext.c [new file with mode: 0644]
modules/other/gail/tests/testtextlib.c [new file with mode: 0644]
modules/other/gail/tests/testtextlib.h [new file with mode: 0644]
modules/other/gail/tests/testtoplevel.c [new file with mode: 0644]
modules/other/gail/tests/testtreetable.c [new file with mode: 0644]
modules/other/gail/tests/testvalues.c [new file with mode: 0644]

index 14421344d176bd4259e38dab9b099dcc6e4066ae..e9826dbb74466440dfbef9fa885c740f41c1285b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2007-12-28  Christian Persch  <chpe@gnome.org>
+
+       * modules/other/gail/Makefile.am:
+       * modules/other/gail/tests/*:
+       * configure.in: Merge tests from standalone gail. Bug #504568.
+
 2007-12-28  Matthias Clasen  <mclasen@redhat.com>
 
        * gtk/gtkclipboard.c: Make the finalizer work when display is
index 6651b2be8773cd3ff6e99923e9df50b169f8da21..c4d56e16a3054e19e4d8235e8a249e0d52b110a1 100644 (file)
@@ -1839,6 +1839,7 @@ modules/Makefile
 modules/other/Makefile
 modules/other/gail/Makefile
 modules/other/gail/libgail-util/Makefile
+modules/other/gail/tests/Makefile
 modules/engines/Makefile
 modules/engines/pixbuf/Makefile
 modules/engines/ms-windows/Makefile
index b864901e965352001bafd2bf9a6838e9b7c448b5..9abc1072489e4ed5e6d1f650675e740a30bc8481 100644 (file)
@@ -1,6 +1,6 @@
 include $(top_srcdir)/Makefile.decl
 
-SUBDIRS = libgail-util
+SUBDIRS = libgail-util tests
 
 if OS_WIN32
 no_undefined = -no-undefined
diff --git a/modules/other/gail/tests/Makefile.am b/modules/other/gail/tests/Makefile.am
new file mode 100644 (file)
index 0000000..d7af911
--- /dev/null
@@ -0,0 +1,226 @@
+if OS_WIN32
+no_undefined = -no-undefined
+endif
+
+if !OS_WIN32
+moduledir = $(libdir)/gtk-2.0/modules
+module_LTLIBRARIES = \
+       libferret.la
+endif
+
+noinst_LTLIBRARIES =           \
+       libtestaction.la        \
+       libtestbutton.la        \
+       libtestcombo.la         \
+       libtestcomponent.la     \
+       libtestimage.la         \
+       libtestnotebook.la      \
+       libtestobject.la        \
+       libtestmenuitem.la      \
+       libtestoptionmenu.la    \
+       libtestpaned.la         \
+       libtestprops.la         \
+       libtestselection.la     \
+       libteststatusbar.la     \
+       libtesttable.la         \
+       libtesttext.la          \
+       libtesttoplevel.la      \
+       libtesttreetable.la     \
+       libtestvalues.la
+
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/gdk     \
+       -I$(top_builddir)/gdk   \
+       -I$(top_srcdir)/gtk     \
+       -I$(top_builddir)/gtk
+
+AM_CFLAGS = \
+       $(GTK_DEP_CFLAGS)       \
+       $(GTK_DEBUG_FLAGS)
+
+if !OS_WIN32
+libferret_la_SOURCES =                 \
+       testlib.c               \
+       testlib.h               \
+       ferret.c
+
+libferret_la_LDFLAGS =         \
+       -rpath $(moduledir) -module -avoid-version \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
+
+libferret_la_LIBADD =          \
+       $(GAIL_INET_LIBS)
+endif
+
+libtestaction_la_SOURCES =     \
+       testaction.c
+
+libtestaction_la_LDFLAGS =     \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
+
+libtestbutton_la_SOURCES =     \
+       testlib.c               \
+       testlib.h               \
+       testbutton.c
+
+libtestbutton_la_LDFLAGS =     \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
+
+libtestcombo_la_SOURCES =      \
+       testlib.c               \
+       testlib.h               \
+       testcombo.c
+
+libtestcombo_la_LDFLAGS =              \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+ $(LDFLAGS)
+
+libtestcomponent_la_SOURCES =  \
+       testcomponent.c
+
+libtestcomponent_la_LDFLAGS =  \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
+
+libtestimage_la_SOURCES =      \
+       testimage.c
+
+libtestimage_la_LDFLAGS =              \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
+
+libtestmenuitem_la_SOURCES =   \
+       testlib.c               \
+       testlib.h               \
+       testmenuitem.c
+
+libtestmenuitem_la_LDFLAGS =           \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
+
+libtestnotebook_la_SOURCES =   \
+       testlib.c               \
+       testlib.h               \
+       testnotebook.c
+
+libtestnotebook_la_LDFLAGS =           \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
+
+libtestobject_la_SOURCES =     \
+       testlib.c               \
+       testlib.h               \
+       testobject.c
+
+libtestobject_la_LDFLAGS =             \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
+
+libtestoptionmenu_la_SOURCES =         \
+       testlib.c               \
+       testlib.h               \
+       testoptionmenu.c
+
+libtestoptionmenu_la_LDFLAGS = \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
+
+libtestpaned_la_SOURCES =      \
+       testlib.c               \
+       testlib.h               \
+       testpaned.c
+
+libtestpaned_la_LDFLAGS =      \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
+
+libtestprops_la_SOURCES =      \
+       testlib.c               \
+       testlib.h               \
+       testprops.c
+
+libtestprops_la_LDFLAGS =      \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
+
+libtestselection_la_SOURCES = \
+       testselection.c
+
+libtestselection_la_LDFLAGS =  \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
+
+libteststatusbar_la_SOURCES = \
+       teststatusbar.c
+
+libteststatusbar_la_LDFLAGS =  \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
+
+libtesttable_la_SOURCES =      \
+       testlib.c               \
+       testlib.h               \
+       testtextlib.c           \
+       testtextlib.h           \
+       testtable.c
+
+libtesttable_la_LDFLAGS =      \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
+
+libtesttext_la_SOURCES =       \
+       testlib.c               \
+       testlib.h               \
+       testtextlib.c           \
+       testtextlib.h           \
+        testtext.c
+
+libtesttext_la_LDFLAGS =       \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
+
+libtesttoplevel_la_SOURCES =   \
+       testlib.c               \
+       testlib.h               \
+       testtoplevel.c
+
+libtesttoplevel_la_LDFLAGS =   \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
+
+libtesttreetable_la_SOURCES =  \
+       testlib.c               \
+       testlib.h               \
+       testtreetable.c
+
+libtesttreetable_la_LDFLAGS =  \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
+
+libtestvalues_la_SOURCES =     \
+       testvalues.c
+
+libtestvalues_la_LDFLAGS =     \
+       -rpath $(moduledir) -module -avoid-version $(no_undefined) \
+       $(GTK_DEP_LIBS) \
+       $(LDFLAGS)
diff --git a/modules/other/gail/tests/README b/modules/other/gail/tests/README
new file mode 100644 (file)
index 0000000..7a6fdcb
--- /dev/null
@@ -0,0 +1,314 @@
+
+============================
+GAIL README
+Last Updated: August 2, 2001
+============================
+
+
+General Info
+============
+
+This README describes how to use the various test programs
+in the gail/tests directory.
+
+To run the various test programs described in this README,
+the test libraries must be built and installed.  Running
+"make", then "make install" in the gail top-level directory
+will take care of this.  Then do the following:
+
+1. Set the environment variable GTK_MODULES to
+   "libgail:lib<testname>"
+
+   For example, for ferret, it would be "libgail:libferret"
+
+2. Run the GTK+ test program specified.  These test programs
+   are found in the GTK+ build directory in the subdirectory
+   called "tests".
+
+Most test programs will display output directly to the 
+terminal window where the GTK+ test program was launched.
+Some test programs (testtable and testtext) will launch a
+test GUI program which allows more interactive testing.
+
+The test GUI has two windows.  The first window is the
+"Test Control" window and the second window is the
+"Test Output" window.  In the "Test Control" window,
+press the button(s) that corresponds to the tests to run
+and press the "Run Tests" button at the bottom of the
+screen.  Some tests have associated text entry fields
+which become active when the button is toggled on.  These
+text entry fields correspond to arguments that affect how
+the test is executed.  They are pre-filled with default
+values but the user can change them if desired.  The
+output from the tests is displayed in the "Test Output"
+window.
+
+
+testlib
+=======
+
+Contains general purpose functionality that is used by the
+various tests.  These include functions that find a specific
+widget/AtkObject in the test program, and functions used by
+tests that use the Gail Test GUI.
+
+
+ferret
+======
+
+Ferret is a passive in-process test tool for ATK and GAIL.
+
+Run a GTK+-2.0 application such as "testgtk", and ferret will
+display a window on screen.   In this window accessibility
+information about the GTK+ widgets will appear as they
+receive focus.
+
+The ferret window has several tabs, one for each of the
+following ATK interfaces.
+
+  Object
+  Action
+  Component
+  Image
+  Table
+  Text
+  Value
+
+Tabs that do not apply to the current widget in focus will be
+displayed as inactive.  Clicking on an active tab will display
+information about the AtkObject accessed via the ATK API.  In the
+Action tab the various actions are displayed as buttons.  When
+a button is clicked, the action specified on the button's
+label is performed.
+
+If you have installed the "festival" speech synthesis system,
+running festival in server mode (festival --server) and turning
+on Ferret's Festival support will cause the following to happen:
+
+1. AtkObject accessible names, roles, and keybindings will be
+   spoken as they receive focus.
+2. When the caret (cursor) is moved in a text field, the 
+   current line will be spoken, unless the caret is moved
+   just a single character.  In the later case, only the
+   single character after the caret will be spoken.
+
+Festival support can be turned on by checking "Festival" in the
+menu, or by setting the environment variable FERRET_FESTIVAL
+prior to starting the test.  By checking "Festival Terse" or
+by setting the environment variable FERRET_TERSE, only the
+name of the AtkObject will be spoken (and not the roles and
+keybindings).
+
+A magnifier can be turned on to enlarge the widget in focus
+by checking "Magnifier" in the menu, or by setting the
+environment variable FERRET_MAGNIFIER.  This requires that
+the magnifier standalone code is running.
+
+Checking "Track Mouse in the menu or by setting the environment
+variable FERRET_MOUSETRACK causes ferret to display information
+about the widget that is under the mouse rather than the widget
+that has focus.  The mouse is tracked via GtkWidget
+"enter_notify_event" signals, so flyweight objects are not tracked.
+
+Checking "Terminal Output" in the menu or by setting the 
+environment variable FERRET_ASCII will display the information
+that is normally displayed to the ferret GUI window to the
+terminal screen.
+
+Checking "No ATK Signals" in the menu or by setting the
+environment variable FERRET_NOSIGNALS will cause ferret to
+ignore any ATK signals, and it will not update its display
+when such signals occur.
+
+
+testaction
+==========
+
+This is a GTK+ module used to test the implementation of the ATK
+interface AtkAction, except for atk_action_do_action() in the GAIL 
+library. It is normally used with the GTK+ test program testgtk.
+
+
+testbutton
+==========
+
+This is a GTK+ module used to test the accessible implementation 
+for buttons. It is normally used with the GTK+ test program testgtk.
+
+Set the TEST_ACCESSIBLE_NAME environment variable to have the test
+driver attach to a widget by widget name (compared via the 
+gtk_widget_get_name function call).
+
+Set the environment variable TEST_ACCESSIBLE_AUTO and the program
+will execute the action defined for a GailButton once.
+
+
+testcombo
+=========
+
+This is a GTK+ module used to test the implementation of the ATK action
+interfaces on GailCombo. It is normally used with the GTK+ test program 
+testgtk by putting the focus in the GtkCombo in entry window.
+
+
+testcomponent
+=============
+
+This is a GTK+ module used to test the implementation of the ATK
+interface AtkComponent in the GAIL library. It is normally used with the 
+GTK+ test program testgtk.
+
+
+testimage
+=========
+
+This is a GTK+ module used to test the implementation of the ATK
+interface AtkImage in the GAIL library. It is normally used with the GTK+
+test program testgtk, but can also be used with testdnd when you want
+to test GtkPixmap. This modules pops up an extra dialog on startup , containing
+GtkArrows and a GtkImage. This dialog has to be closed before control is returned to main window.
+
+
+testmenuitem
+============
+
+This is a GTK+ module used to test the accessible implementation 
+for menu items. It is normally used with the GTK+ test program testgtk.
+
+Set the TEST_ACCESSIBLE_NAME environment variable to have the test
+driver attach to a widget by widget name (compared via the 
+gtk_widget_get_name function call).
+
+Set the environment variable TEST_ACCESSIBLE_AUTO and the program
+will execute the action defined for a GailButton once.
+
+
+testnotebook
+=============
+
+This is a GTK+ module used to test the implementation of the ATK
+interface AtkSelection for GailNotebook. It is normally used with the 
+GTK+ test program testgtk.
+
+
+testobject
+==========
+
+This is a GTK+ module used to test the implementation of the ATK
+interface in atkobject.h in the GAIL library. It is normally used with the 
+GTK+ test program testgtk.
+
+
+testoptionmenu
+==============
+
+This is a GTK+ module used to test the implementation of the ATK interfaces
+for GtkOptionmenu. It should be used with the GTK+ test program testgtk and
+its file selection dialog.
+
+
+testpaned
+=========
+
+This is a GTK+ module used to test the implementation of the ATK
+interface AtkValue for GailPaned. It is normally used with the 
+GTK+ test program testgtk. It checks the setting of the position 
+programmatically and that notification is received if the position
+is changed interactively.
+
+
+testprops
+==========
+
+This is a GTK+ module used to test the implementation of ATK properties 
+and property change handlers in the GAIL library. It is normally used with 
+the GTK+ test program testgtk. To see the changing of the state
+ATK_STATE_SHOWING use menus in "progress bar". To see the changing of the
+state ATK_STATE_SENSITIVE uses "labels". To see changing of child and parent
+use resize check box in "panes".
+
+Set the TEST_ACCESSIBLE_NAME environment variable to have the test
+driver attach to a widget by widget name (compared via the 
+gtk_widget_get_name function call).
+
+
+testselection
+=============
+
+This is a GTK+ module used to test the implementation of the AtkSelection
+interface works for the GAIL library. It is normally used with the GTK+ 
+test program testgtk and clicking on the menus option. It can also be used
+with the GtkCombo which can be accessed by clicking on the entry option.
+
+
+teststatusbar
+=============
+
+This is a GTK+ module used to test that the text on the statusbar
+can be retrieved using GailStatusbar. It is normally used with the GTK+ 
+test program testgtk and clicking on statusbar button.
+
+
+testtable
+=========
+
+This is GTK+ module used to test the implementation of AtkTable
+interfaces.  It can be used with GailTreeView, for example.  It
+can be used with any of the following GTK+ test programs:
+testtreecolumns, testtreefocus, testtreesort, testtreeview,
+or treestoretest.
+
+Set the TEST_ACCESSIBLE_NO_PROPERTIES environment variable
+to not receive information about property values changing
+(like cell state changes).
+
+Set the TEST_ACCESSIBLE_NO_GUI environment variable to run the
+test without the GUI program.
+
+
+testtext
+========
+
+This is a GTK+ module used to test the implementation of AtkText and 
+AtkEditableText interfaces on GailTextView.  It is normally used with 
+the GTK+ test program testtext started with a text file loaded.
+It can also be used with the GTK+ test program testgtk, and then
+click on the "entry" or "label" button.
+
+Set the TEST_ACCESSIBLE_NAME environment variable to have the test
+driver attach to a widget by widget name (compared via the 
+gtk_widget_get_name function call).
+
+Set the TEST_ACCESSIBLE_DELAY environment variable to an integer
+and the test driver will attach to only a widget on the nth screen
+that is displayed.
+
+
+testtoplevel
+============
+
+This test exercises the AtkUtil functions.  It accesses the
+atk_get_root() toplevel object, sets/removes global listeners,
+and displays the ATK implementation name/version.
+
+Set the TEST_ACCESSIBLE_DEPTH environment variable to control
+how deep the children of the toplevel object are displayed.
+The default is a depth of 2.  Specifying a depth of -1 will
+show the complete hierarchy.
+
+
+testvalues
+==========
+
+This is a GTK+ module used to test the implementation of AtkValue interface
+works for the GAIL library. GailProgressbar, GailSpinbutton and GailRange
+can all be tested using this module.
+
+
+GAIL README Authors
+===================
+
+-Brian Cameron (brian.cameron@sun.com)
+-Bill Haneman (bill.haneman@sun.com)
+-Padraig O'Briain (padraig.obriain@sun.com)
+
diff --git a/modules/other/gail/tests/ferret.c b/modules/other/gail/tests/ferret.c
new file mode 100644 (file)
index 0000000..73ce9da
--- /dev/null
@@ -0,0 +1,2690 @@
+#define MAX_BUFFER 256
+#define GTK_ENABLE_BROKEN
+#define MAX_GROUPS 20
+#define MAX_NAME_VALUE 20
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <strings.h>
+#include "testlib.h"
+#include "config.h"
+
+typedef enum
+{
+  OBJECT,
+  ACTION,
+  COMPONENT,
+  IMAGE,
+  SELECTION,
+  TABLE,
+  TEXT,
+  VALUE,
+  END_TABS
+} TabNumber;
+
+typedef enum
+{
+  OBJECT_INTERFACE,
+  RELATION_INTERFACE,
+  STATE_INTERFACE,
+  ACTION_INTERFACE,
+  COMPONENT_INTERFACE,
+  IMAGE_INTERFACE,
+  SELECTION_INTERFACE,
+  TABLE_INTERFACE,
+  TEXT_INTERFACE,
+  TEXT_ATTRIBUTES,
+  VALUE_INTERFACE
+} GroupId;
+
+typedef enum
+{
+  VALUE_STRING,
+  VALUE_BOOLEAN,
+  VALUE_TEXT,
+  VALUE_BUTTON
+} ValueType;
+
+/* GUI Information for the group */
+
+typedef struct
+{
+  GroupId       group_id;
+  GtkFrame      *scroll_outer_frame;
+  GtkWidget     *frame;
+  GtkVBox       *group_vbox;
+  GtkAdjustment *adj;
+  GList         *name_value;
+  gchar         *name;
+  gboolean      is_scrolled;
+  gint          default_height;
+} GroupInfo;
+
+typedef struct
+{
+  GList     *groups;
+  GtkWidget *page;
+  GtkWidget *main_box;
+  gchar     *name;
+} TabInfo;
+
+typedef struct
+{
+  ValueType type;
+  gboolean  active;
+
+  GtkHBox *column1, *column2, *hbox;
+  GtkLabel *label;
+
+  GtkButton *button;
+  GValue    button_gval;
+  gulong    signal_id;
+  AtkObject *atkobj;
+  gint      action_num;
+
+  GtkWidget *string;
+  GtkWidget *boolean;
+  GtkWidget *text;
+} NameValue;
+
+typedef enum {
+   FERRET_SIGNAL_OBJECT,
+   FERRET_SIGNAL_TEXT,
+   FERRET_SIGNAL_TABLE
+} FerretSignalType;
+
+/* Function prototypes */
+
+/* GUI functions */
+
+static void _init_data(void);
+static void _create_window(void);
+static void _add_menu(GtkWidget ** menu, GtkWidget ** menuitem,
+  gchar * name, gboolean init_value, GCallback func);
+static void _clear_tab(TabNumber tab_n);
+static void _greyout_tab (GtkWidget *widget, gboolean is_sensitive);
+static void _finished_group(TabNumber tab_n, gint group_num);
+static gboolean _object_is_ours (AtkObject *aobject);
+static void _create_event_watcher (void);
+
+/* Mouse Watcher/Magnifier/Festival functions */
+
+static gboolean _mouse_watcher (GSignalInvocationHint *ihint,
+       guint                  n_param_values,
+       const GValue          *param_values,
+       gpointer               data);
+static gboolean _button_watcher (GSignalInvocationHint *ihint,
+       guint                  n_param_values,
+       const GValue          *param_values,
+       gpointer               data);
+static void _send_to_magnifier (gint x, gint y, gint w, gint h);
+static void _send_to_festival (const gchar * name,
+  const gchar * role_name, const gchar * accel);
+static void _speak_caret_event (AtkObject * aobject);
+static void _festival_say (const gchar * text);
+static void _festival_write (const gchar * text, int fd);
+static gint _festival_init (void);
+
+/* Update functions */
+
+static void _update_current_page(GtkNotebook *notebook, gpointer p,
+  guint current_page);
+static void _update(TabNumber top_tab, AtkObject *aobject);
+
+/* Print functions */
+
+static void _print_accessible (AtkObject *aobject);
+
+static gint _print_object (AtkObject *aobject);
+static gint _print_relation (AtkObject *aobject);
+static gint _print_state (AtkObject *aobject);
+
+static gint _print_action (AtkAction *aobject);
+static gint _print_component (AtkComponent *aobject);
+static gint _print_image (AtkImage *aobject);
+static gint _print_selection (AtkSelection *aobject);
+static gint _print_table (AtkTable *aobject);
+static gint _print_text (AtkText *aobject);
+static gint _print_text_attributes (AtkText *aobject);
+static gint _print_value (AtkValue *aobject);
+static void _print_value_type(gint group_num, gchar *type, GValue *value);
+static gint _print_groupname(TabNumber tab_n, GroupId group_id,
+  const char *groupname);
+static NameValue* _print_key_value(TabNumber tab_n, gint group_number,
+  const char *label, gpointer value, ValueType type);
+static void _print_signal(AtkObject *aobject, FerretSignalType type,
+  const char *name, const char *info);
+
+/* Data Access functions */
+
+static GroupInfo* _get_group(TabInfo *tab, GroupId group_id,
+  const gchar *groupname);
+void _get_group_scrolled(GroupInfo *group);
+static NameValue* _get_name_value(GroupInfo *group, const gchar *label,
+  gpointer value, ValueType type);
+
+/* Signal handlers */
+
+static void _update_handlers(AtkObject *obj);
+static void _notify_text_insert_handler (GObject *obj,
+  int position, int offset);
+static void _notify_text_delete_handler (GObject *obj,
+  int position, int offset);
+static void _notify_caret_handler (GObject *obj, int position);
+static void _notify_table_row_inserted (GObject *obj,
+  gint start_offset, gint length);
+static void _notify_table_column_inserted (GObject *obj,
+  gint start_offset, gint length);
+static void _notify_table_row_deleted (GObject *obj,
+  gint start_offset, gint length);
+static void _notify_table_column_deleted (GObject *obj,
+  gint start_offset, gint length);
+static void _notify_table_row_reordered (GObject *obj);
+static void _notify_table_column_reordered (GObject *obj);
+static void _notify_object_child_added (GObject *obj,
+  gint index, AtkObject *child);
+static void _notify_object_child_removed (GObject *obj,
+  gint index, AtkObject *child);
+static void _notify_object_state_change (GObject *obj,
+  gchar *name, gboolean set);
+
+/* Property handlers */
+
+static void _property_change_handler (AtkObject *obj,
+  AtkPropertyValues *values);
+
+/* Ferret GUI callbacks */
+
+void _action_cb(GtkWidget *widget, gpointer  *userdata);
+void _toggle_terminal(GtkCheckMenuItem *checkmenuitem,
+  gpointer user_data);
+void _toggle_no_signals(GtkCheckMenuItem *checkmenuitem,
+  gpointer user_data);
+void _toggle_magnifier(GtkCheckMenuItem *checkmenuitem,
+  gpointer user_data);
+void _toggle_festival(GtkCheckMenuItem *checkmenuitem,
+  gpointer user_data);
+void _toggle_festival_terse(GtkCheckMenuItem *checkmenuitem,
+  gpointer user_data);
+void _toggle_trackmouse(GtkCheckMenuItem *checkmenuitem,
+  gpointer user_data);
+void _toggle_trackfocus(GtkCheckMenuItem *checkmenuitem,
+  gpointer user_data);
+
+/* Global variables */
+static GtkNotebook *notebook;
+static TabInfo  *nbook_tabs[END_TABS];
+static gint mouse_watcher_focus_id = -1;
+static gint mouse_watcher_button_id = -1;
+static gint focus_tracker_id = -1;
+static gboolean use_magnifier = FALSE;
+static gboolean use_festival = FALSE;
+static gboolean track_mouse = FALSE;
+static gboolean track_focus = TRUE;
+static gboolean say_role = TRUE;
+static gboolean say_accel = TRUE;
+static gboolean display_ascii = FALSE;
+static gboolean no_signals = FALSE;
+static gint last_caret_offset = 0;
+
+static AtkObject *last_object = NULL;
+static GtkWidget *mainWindow = NULL;
+static GtkWidget *vbox1 = NULL;
+static GtkWidget *menu = NULL;
+static GtkWidget *menutop = NULL;
+static GtkWidget *menubar = NULL;
+static GtkWidget *menuitem_terminal = NULL;
+static GtkWidget *menuitem_no_signals = NULL;
+static GtkWidget *menuitem_magnifier = NULL;
+static GtkWidget *menuitem_festival = NULL;
+static GtkWidget *menuitem_festival_terse = NULL;
+static GtkWidget *menuitem_trackmouse = NULL;
+static GtkWidget *menuitem_trackfocus = NULL;
+
+#ifdef HAVE_SOCKADDR_UN_SUN_LEN
+static struct sockaddr_un mag_server = { 0, AF_UNIX , "/tmp/magnifier_socket" };
+static struct sockaddr_un client = { 0 , AF_UNIX, "/tmp/mag_client"};
+#else
+static struct sockaddr_un mag_server = { AF_UNIX , "/tmp/magnifier_socket" };
+static struct sockaddr_un client = { AF_UNIX, "/tmp/mag_client"};
+#endif
+
+/* GUI Information for the output window */
+typedef struct
+{
+  GtkWindow     *outputWindow;
+  GtkWidget     *hbox;
+  GtkWidget     *vbox;
+  GtkWidget     *label;
+  GtkWidget     *textInsert;
+  gchar         *testTitle;
+} MainDialog;
+
+static void
+_send_to_magnifier(gint x, gint y, gint w, gint h)
+{
+  int desc, length_msg;
+  gchar buff[100];
+
+  sprintf (buff, "~5:%d,%d", x+w/2, y+h/2);
+
+#ifdef MAG_DEBUG
+  g_print ("sending magnifier: %s\n", buff);
+#endif
+
+#ifdef HAVE_SOCKADDR_UN_SUN_LEN
+  mag_server.sun_len = SUN_LEN(&mag_server);
+  client.sun_len = SUN_LEN(&client);
+#endif
+  
+  if((desc=socket(AF_UNIX,SOCK_STREAM,0)) == -1){
+    perror("socket");
+    return;
+  }
+  unlink("/tmp/mag_client");
+
+  if (bind(desc, (struct sockaddr*)&client, sizeof(client)) == -1)
+    {
+      perror("bind");
+      return;
+    }
+
+  if (connect(desc,(struct sockaddr*)&mag_server,sizeof(mag_server)) == -1)
+    {
+      perror("connect");
+      return;
+    }
+
+  length_msg = write(desc,buff,strlen(buff));
+  unlink("/tmp/mag_client");
+  return;
+}
+
+static int _festival_init (void)
+{
+  int fd;
+  struct sockaddr_in name;
+  int tries = 2;
+
+  name.sin_family = AF_INET;
+  name.sin_port = htons (1314);
+  name.sin_addr.s_addr = htonl(INADDR_ANY);
+  fd = socket (PF_INET, SOCK_STREAM, 0);
+
+  while (connect(fd, (struct sockaddr *) &name, sizeof (name)) < 0) {
+    if (!tries--) {
+      perror ("connect");
+      return -1;
+    }
+  }
+
+  _festival_write ("(audio_mode'async)", fd);
+  return fd;
+}
+
+static void _festival_say (const gchar *text)
+{
+  static int fd = 0;
+  gchar *quoted;
+  gchar *p;
+  gchar prefix [100];
+  const gchar *stretch;
+
+  fprintf (stderr, "saying %s\n", text);
+
+  if (!fd)
+    {
+      fd = _festival_init ();
+    }
+
+  quoted = g_malloc(100+strlen(text)*2);
+
+  stretch = g_strdup (g_getenv ("FESTIVAL_STRETCH"));
+  if (!stretch) stretch = "0.75";
+  sprintf (prefix, "(audio_mode'shutup)\n (Parameter.set 'Duration_Stretch %s)\n (SayText \"", stretch);
+  
+  strcpy(quoted, prefix);
+  p = quoted + strlen (prefix);
+  while (*text) {
+    if ( *text == '\\' || *text == '"' )
+      *p = '\\';
+    *p++ = *text++;
+  }
+  *p++ = '"';
+  *p++ = ')';
+  *p = 0;
+
+  _festival_write (quoted, fd);
+
+  g_free(quoted);
+}
+
+
+static void _send_to_festival (const gchar *role_name,
+  const gchar *name, const gchar *accel)
+{
+  gchar *string;
+  int len = (strlen (role_name)+1 + strlen (name)+2 + 4 + strlen (accel)+2);
+  int i, j;
+  gchar ch;
+  gchar *accel_name;
+  
+  string = (gchar *) g_malloc (len * sizeof (gchar));
+
+  i = 0;
+  if (say_role)
+    {
+      j = 0;
+      while (role_name[j])
+        {
+          ch = role_name[j++];
+          if (ch == '_') ch = ' ';
+          string[i++] = ch;
+        };
+      string[i++] = ' ';
+    }
+  j = 0;
+  while (name[j])
+    {
+      ch = name[j++];
+      if (ch == '_') ch = ' ';
+      string[i++] = ch;
+    };
+  if ((say_accel) && (strlen (accel) > 0))
+    {
+      accel_name = (gchar *)accel;
+      if (strncmp (accel, "<C", 2) == 0)
+        {
+          accel_name = strncpy (accel_name, " control ", 9);
+        }
+      else if (strncmp (accel, " control", 5))
+        {
+          string[i++] = ' ';
+          string[i++] = 'a';
+          string[i++] = 'l';
+          string[i++] = 't';
+          string[i++] = ' ';
+        }
+      j = 0;
+      while (accel_name[j])
+        {
+          ch = accel_name[j++];
+          if (ch == '_') ch = ' ';
+          string[i++] = ch;
+        };
+    }
+  string[i] = '\0';
+
+  _festival_say (string);
+  g_free (string);
+}
+
+static void _festival_write (const gchar *command_string, int fd)
+{
+  if (fd < 0) {
+    perror("socket");
+    return;
+  }
+  write(fd, command_string, strlen(command_string));
+}
+
+static void _speak_caret_event (AtkObject *aobject)
+{
+  gint dummy1, dummy2;
+  gint caret_offset = atk_text_get_caret_offset (ATK_TEXT (aobject));
+  gchar * text;
+
+  if (abs(caret_offset - last_caret_offset) > 1)
+    {
+      text = atk_text_get_text_at_offset (ATK_TEXT (aobject),
+                                              caret_offset,
+                                              ATK_TEXT_BOUNDARY_LINE_START,
+                                              &dummy1,
+                                              &dummy2);
+    }
+  else
+    {
+      text = atk_text_get_text_before_offset (ATK_TEXT (aobject),
+                                              caret_offset,
+                                              ATK_TEXT_BOUNDARY_CHAR,
+                                              &dummy1,
+                                              &dummy2);
+    }
+  _festival_say (text);
+  g_free (text);
+  last_caret_offset = caret_offset;
+}
+
+static void
+_greyout_tab (GtkWidget *page_child, gboolean is_sensitive)
+{
+  GtkWidget *tab;
+
+  tab = gtk_notebook_get_tab_label (notebook, page_child);
+  if (tab)
+      gtk_widget_set_sensitive (GTK_WIDGET (tab), is_sensitive);
+}
+
+static void
+_refresh_notebook (AtkObject *aobject)
+{
+  if (ATK_IS_OBJECT (aobject))
+  {
+    _greyout_tab (nbook_tabs[ACTION]->page, ATK_IS_ACTION(aobject));
+    _greyout_tab (nbook_tabs[COMPONENT]->page, ATK_IS_COMPONENT(aobject));
+    _greyout_tab (nbook_tabs[IMAGE]->page, ATK_IS_IMAGE(aobject));
+    _greyout_tab (nbook_tabs[SELECTION]->page, ATK_IS_SELECTION(aobject));
+    _greyout_tab (nbook_tabs[TABLE]->page, ATK_IS_TABLE(aobject));
+    _greyout_tab (nbook_tabs[TEXT]->page, ATK_IS_TEXT(aobject));
+    _greyout_tab (nbook_tabs[VALUE]->page, ATK_IS_VALUE(aobject));
+  }
+}
+
+static void _print_accessible (AtkObject *aobject)
+{
+  TabNumber top_tab;
+
+  if (_object_is_ours(aobject))
+    {
+      if (display_ascii)
+        g_print("\nFocus entered the ferret output window!\n");
+      return;
+    }
+
+  _refresh_notebook(aobject);
+
+  if (display_ascii)
+    g_print("\nFocus change\n");
+
+  /* Do not attach signal handlers if the user has asked not to */
+  if (!no_signals)
+    _update_handlers(aobject);
+  else
+    last_object = aobject; /* _update_handler normally does this */
+
+  top_tab = gtk_notebook_get_current_page (notebook) + OBJECT;
+  _update(top_tab, aobject);
+
+  if (use_magnifier)
+    {
+      gint x, y;
+      gint w=0, h=0;
+      
+      if (ATK_IS_TEXT (aobject))
+        {
+         gint x0, y0, w0, h0;
+         gint xN, yN, wN, hN;
+         gint len;
+         len = atk_text_get_character_count (ATK_TEXT (aobject));
+         atk_text_get_character_extents (ATK_TEXT (aobject), 0,
+                                         &x0, &y0, &w0, &h0,
+                                         ATK_XY_SCREEN);
+          if (len > 0)
+           {
+             atk_text_get_character_extents (ATK_TEXT (aobject), len-1,
+                                             &xN, &yN, &wN, &hN,
+                                             ATK_XY_SCREEN);
+             x = MIN (x0, xN);
+             y = MIN (y0, yN);
+             w = MAX (x0+w0, xN+wN) - x;
+             h = MAX (y0+h0, yN+hN) - y;
+           }
+          else
+           {
+             x = x0;
+             y = y0;
+           }
+        } 
+      else if (ATK_IS_COMPONENT (aobject))
+        {
+         atk_component_get_extents (ATK_COMPONENT(aobject),
+                                    &x, &y, &w, &h,
+                                    ATK_XY_SCREEN);
+        }
+      if (w > -1) _send_to_magnifier (x, y, w, h);
+    }
+}
+
+static gboolean
+_object_is_ours (AtkObject *aobject)
+{
+  /* determine whether this object is parented by our own accessible... */
+
+   AtkObject *toplevel = aobject;
+
+   while (atk_object_get_role(aobject) != ATK_ROLE_FRAME)
+     {
+       aobject = atk_object_get_parent (aobject);
+       if (aobject == NULL) break;
+       toplevel = aobject;
+     };
+
+  /*
+   * Some widgets do not have an ATK_ROLE_FRAME at the top,
+   * so ignore those.
+   */
+   if (aobject != NULL)
+     {
+       if (GTK_ACCESSIBLE(toplevel)->widget == mainWindow)
+         {
+           return TRUE;
+         }
+     }
+
+  return FALSE;
+}
+
+static gchar *
+ferret_get_name_from_container (AtkObject *aobject)
+{
+  gchar *s = NULL;
+  gint n = atk_object_get_n_accessible_children (aobject);
+  gint i = 0;
+  
+  while (!s && (i < n))
+    {
+      AtkObject *child;            
+      child = atk_object_ref_accessible_child (aobject, i);
+      if (ATK_IS_TEXT (child))
+        {
+               gint count = atk_text_get_character_count (ATK_TEXT (child));
+               s = atk_text_get_text (ATK_TEXT (child),
+                                      0,
+                                      count);
+        }
+      g_object_unref (child);
+      ++i;         
+    }
+  
+  if (!s)
+    {
+      s = g_strdup ("");
+    }
+  return s;    
+}
+
+static gint
+_print_object (AtkObject *aobject)
+{
+    G_CONST_RETURN gchar * parent_name = NULL;
+    G_CONST_RETURN gchar * name = NULL;
+    G_CONST_RETURN gchar * description = NULL;
+    G_CONST_RETURN gchar * typename = NULL;
+    G_CONST_RETURN gchar * parent_typename = NULL;
+    G_CONST_RETURN gchar * role_name = NULL;
+    G_CONST_RETURN gchar * accel_name = NULL;
+    G_CONST_RETURN gchar * text = NULL;
+    AtkRole role;
+    AtkObject *parent = NULL;
+    static AtkObject *prev_aobject = NULL;
+    gint n_children = 0;
+    gint index_in_parent = -1;
+    gchar *output_str;
+    gint group_num;
+    TabNumber tab_n = OBJECT;
+
+    group_num = _print_groupname(tab_n, OBJECT_INTERFACE, "Object Interface");
+
+    name = atk_object_get_name (aobject);
+    typename = g_type_name (G_OBJECT_TYPE (aobject));
+    description = atk_object_get_description (aobject);
+    parent = atk_object_get_parent(aobject);
+    if (parent)
+      index_in_parent = atk_object_get_index_in_parent(aobject);
+    n_children = atk_object_get_n_accessible_children(aobject);
+    role = atk_object_get_role(aobject);
+    role_name = atk_role_get_name(role);
+
+    if (ATK_IS_ACTION (aobject))
+      {
+        accel_name = atk_action_get_keybinding (ATK_ACTION(aobject), 0);
+        if (!accel_name) accel_name = "";
+      }
+    else
+      {
+        accel_name = "";
+      }
+
+    if (GTK_IS_ACCESSIBLE (aobject) &&
+        GTK_IS_WIDGET (GTK_ACCESSIBLE (aobject)->widget))
+      {
+        _print_key_value(tab_n, group_num, "Widget name",
+          (gpointer)gtk_widget_get_name(GTK_ACCESSIBLE (aobject)->widget),
+          VALUE_STRING);
+      }
+    else
+      {
+        _print_key_value(tab_n, group_num, "Widget name", "No Widget",
+          VALUE_STRING);
+      }
+
+    if (typename)
+      {
+        _print_key_value(tab_n, group_num, "Accessible Type",
+          (gpointer)typename, VALUE_STRING);
+      }
+    else
+      {
+        _print_key_value(tab_n, group_num, "Accessible Type", "NULL",
+          VALUE_STRING);
+      }
+
+    if (name)
+      {
+        _print_key_value(tab_n, group_num, "Accessible Name",
+          (gpointer)name, VALUE_STRING);
+      }
+    else
+      {
+        _print_key_value(tab_n, group_num, "Accessible Name", "(unknown)",
+          VALUE_STRING);
+      }
+    if (use_festival)
+      {
+        if (aobject != prev_aobject)
+          {
+           if (ATK_IS_TEXT (aobject) && !name)
+             {
+               text = 
+                 atk_text_get_text_at_offset (ATK_TEXT (aobject),
+                                              (gint) 0,
+                                              ATK_TEXT_BOUNDARY_SENTENCE_END,
+                                              (gint *) NULL,
+                                              (gint *) NULL);
+               fprintf (stderr, "first sentence: %s\n", text);
+               _send_to_festival (role_name, 
+                                  text, "");
+               if (!name) name = "no name";
+             }
+            else 
+             { 
+               text = "";
+               if (!name)
+                 {
+                   if (atk_object_get_role (aobject) == ATK_ROLE_TABLE_CELL)
+                     {
+                       gchar *cname = ferret_get_name_from_container (aobject);
+                       if (cname) name = g_strdup (cname);
+                     }
+                   else if (atk_object_get_role (aobject) == ATK_ROLE_CHECK_BOX)
+                     {
+                       name = g_strdup ("check box");
+                     }
+                   else
+                     {
+                       name = "no name";
+                     }
+                 }
+               _send_to_festival (role_name, name, accel_name);
+             }
+          }
+      }
+
+    if (parent)
+      {
+        parent_name = atk_object_get_name(parent);
+
+        parent_typename = g_type_name (G_OBJECT_TYPE (parent));
+
+        if (parent_typename)
+          {
+            _print_key_value(tab_n, group_num, "Parent Accessible Type",
+              (gpointer)parent_typename, VALUE_STRING);
+          }
+        else
+          {
+            _print_key_value(tab_n, group_num,
+              "Parent Accessible Type", "NULL", VALUE_STRING);
+          }
+
+        if (parent_name)
+          {
+            _print_key_value(tab_n, group_num, "Parent Accessible Name",
+              (gpointer)parent_name, VALUE_STRING);
+          }
+        else
+          {
+            _print_key_value(tab_n, group_num,
+              "Parent Accessible Name", "NULL", VALUE_STRING);
+          }
+
+        output_str = g_strdup_printf("%d", index_in_parent);
+        _print_key_value(tab_n, group_num, "Index in Parent",
+          (gpointer)output_str, VALUE_STRING);
+        g_free(output_str);
+      }
+    else
+      {
+        _print_key_value(tab_n, group_num, "Parent", "NULL", VALUE_STRING);
+      }
+
+    if (description)
+      {
+        _print_key_value(tab_n, group_num, "Accessible Description",
+          (gpointer)description, VALUE_STRING);
+      }
+    else
+      {
+        _print_key_value(tab_n, group_num,
+          "Accessible Description", "NULL", VALUE_STRING);
+      }
+
+    if (role_name)
+      {
+      _print_key_value(tab_n, group_num, "Accessible Role", (gpointer)role_name,
+        VALUE_STRING);
+      }
+    else
+      {
+      _print_key_value(tab_n, group_num, "Accessible Role", "NULL",
+        VALUE_STRING);
+      }
+
+    output_str = g_strdup_printf("%d", n_children);
+    _print_key_value(tab_n, group_num, "Number Children", (gpointer)output_str,
+       VALUE_STRING);
+    g_free(output_str);
+    prev_aobject = aobject;
+
+    return(group_num);
+}
+
+static gint
+_print_relation (AtkObject *aobject)
+{
+    AtkRelationSet* relation_set = atk_object_ref_relation_set (aobject);
+    gint n_relations =  atk_relation_set_get_n_relations (relation_set);
+    gint group_num;
+    TabNumber tab_n = OBJECT;
+
+    group_num = _print_groupname(tab_n, RELATION_INTERFACE,
+      "Relation Interface");
+
+    if (relation_set)
+      {
+        AtkRelation * relation;
+        G_CONST_RETURN gchar * relation_name = NULL;
+        G_CONST_RETURN gchar * relation_obj_name = NULL;
+        AtkRelationType relation_type;
+        AtkObject *relation_obj;
+        GPtrArray * relation_arry;
+        gchar *label_str;
+        gchar *output_str;
+        gint i, j;
+
+        output_str = g_strdup_printf("%d", n_relations);
+        _print_key_value(tab_n, group_num,
+          "Number of Relations", (gpointer)output_str, VALUE_STRING);
+        g_free(output_str);
+
+        for (i = 0; i < n_relations; i++)
+          {
+            relation = atk_relation_set_get_relation (relation_set, i);
+
+            relation_type = atk_relation_get_relation_type (relation);
+            relation_name = atk_relation_type_get_name (relation_type);
+
+            relation_arry = atk_relation_get_target(relation);
+
+            if (relation_name)
+              {
+                label_str = g_strdup_printf("Relation %d Name", i + 1);
+                _print_key_value(tab_n, group_num, label_str,
+                  (gpointer)relation_name, VALUE_STRING);
+                g_free(label_str);
+              }
+            else
+              {
+                label_str = g_strdup_printf("Relation %d Type", i + 1);
+                output_str = g_strdup_printf("%d", relation_type);
+                _print_key_value(tab_n, group_num, label_str,
+                  (gpointer)output_str, VALUE_STRING);
+                g_free(label_str);
+                g_free(output_str);
+              }
+
+            label_str = g_strdup_printf("Relation %d with", i + 1);
+            output_str = g_strdup_printf("%d AtkObjects", relation_arry->len);
+            _print_key_value(tab_n, group_num, label_str, (gpointer)output_str,
+              VALUE_STRING);
+            g_free(label_str);
+            g_free(output_str);
+
+            for (j=0; j < relation_arry->len; j++)
+              {
+                label_str = g_strdup_printf(
+                  "Relation %d,%d with AtkObject Name", i + 1, j + 1);
+                relation_obj = (AtkObject *)
+                   g_ptr_array_index(relation_arry, j);
+                relation_obj_name = atk_object_get_name(relation_obj);
+
+                _print_key_value(tab_n, group_num, label_str,
+                  (gpointer)relation_obj_name, VALUE_STRING);
+                g_free(label_str);
+              }
+          }
+
+        g_object_unref (relation_set);
+      }
+    return(group_num);
+}
+
+static gint
+_print_state (AtkObject *aobject)
+{
+    AtkStateSet *state_set = atk_object_ref_state_set(aobject);
+    gint group_num;
+    TabNumber tab_n = OBJECT;
+    static AtkStateType states_to_track[] =
+      {
+        ATK_STATE_ACTIVE,
+        ATK_STATE_CHECKED,
+        ATK_STATE_EXPANDED,
+        ATK_STATE_EXPANDABLE,
+        ATK_STATE_SELECTED,
+        ATK_STATE_SHOWING,
+        ATK_STATE_VISIBLE
+      };
+
+    group_num = _print_groupname(tab_n, STATE_INTERFACE,
+      "State Interface");
+
+    if (state_set)
+      {
+        gboolean boolean_value;
+        AtkStateType one_state;
+        G_CONST_RETURN gchar *name;
+        gint i;
+
+        for (i=0; i < sizeof(states_to_track)/sizeof(AtkStateType); i++)
+          {
+            one_state = (AtkStateType) states_to_track[i];
+            name = atk_state_type_get_name (one_state);
+
+            if (name)
+              {
+                boolean_value =
+                  atk_state_set_contains_state (state_set, one_state);
+                _print_key_value(tab_n, group_num, name,
+                  (gpointer)(&boolean_value), VALUE_BOOLEAN);
+              }
+          }
+      }
+
+    g_object_unref (state_set);
+    return(group_num);
+}
+
+static gint
+_print_action (AtkAction *aobject)
+{
+    G_CONST_RETURN gchar *action_name;
+    G_CONST_RETURN gchar *action_description;
+    G_CONST_RETURN gchar *action_keybinding;
+    gchar *label_str, *output_str;
+    gint group_num;
+    gint num_actions, j;
+    TabNumber tab_n = ACTION;
+    NameValue *nv;
+
+    group_num = _print_groupname(tab_n, ACTION_INTERFACE,
+      "Action Interface");
+
+    num_actions = atk_action_get_n_actions (aobject);
+    output_str = g_strdup_printf("%d", num_actions);
+    _print_key_value(tab_n, group_num, "Number of Actions",
+      (gpointer) output_str, VALUE_STRING);
+    g_free(output_str);
+
+    for (j = 0; j < num_actions; j++)
+      {
+        label_str = g_strdup_printf("Action %d Name", j + 1);
+        action_name = atk_action_get_name (aobject, j);
+        if (action_name)
+          {
+            nv = _print_key_value(tab_n, group_num, label_str,
+             (gpointer) action_name, VALUE_BUTTON);
+          }
+        else
+          {
+            nv = _print_key_value(tab_n, group_num, label_str, "NULL",
+              VALUE_BUTTON);
+          }
+
+        nv->atkobj = ATK_OBJECT(aobject);
+        nv->action_num = j;
+        nv->signal_id = g_signal_connect (GTK_OBJECT (nv->button),
+          "clicked", GTK_SIGNAL_FUNC (_action_cb), nv);
+
+        g_free(label_str);
+
+        label_str = g_strdup_printf("Action %d Description", j + 1);
+        action_description = atk_action_get_description (aobject, j);
+        if (action_description)
+          {
+            _print_key_value(tab_n, group_num, label_str,
+              (gpointer)action_description, VALUE_STRING);
+          }
+        else
+          {
+            _print_key_value(tab_n, group_num, label_str, "NULL",
+              VALUE_STRING);
+          }
+        g_free(label_str);
+
+        label_str = g_strdup_printf("Action %d Keybinding", j + 1);
+        action_keybinding = atk_action_get_keybinding (aobject, j);
+        if (action_keybinding)
+          {
+            _print_key_value(tab_n, group_num, label_str,
+              (gpointer)action_keybinding, VALUE_STRING);
+          }
+        else
+          {
+            _print_key_value(tab_n, group_num, label_str, "NULL",
+              VALUE_STRING);
+          }
+        g_free(label_str);
+      }
+    return(group_num);
+}
+
+static gint
+_print_component (AtkComponent *aobject)
+{
+    gchar *output_str;
+    gint x = 0;
+    gint y = 0;
+    gint width = 0;
+    gint height = 0;
+    gint group_num;
+    TabNumber tab_n = COMPONENT;
+
+    group_num = _print_groupname(tab_n, COMPONENT_INTERFACE,
+      "Component Interface");
+
+    atk_component_get_extents (aobject,
+       &x, &y, &width, &height, ATK_XY_SCREEN);
+
+    output_str = g_strdup_printf("x: %d y: %d width: %d height %d",
+      x, y, width, height);
+    _print_key_value(tab_n, group_num, "Geometry", (gpointer)output_str,
+      VALUE_STRING);
+    g_free(output_str);
+    return(group_num);
+}
+
+static gint
+_print_image (AtkImage *aobject)
+{
+    G_CONST_RETURN gchar *image_desc;
+    gchar *output_str;
+    gint x = 0;
+    gint y = 0;
+    gint height = 0;
+    gint width = 0;
+    gint group_num;
+    TabNumber tab_n = IMAGE;
+
+    group_num = _print_groupname(tab_n, IMAGE_INTERFACE,
+      "Image Interface");
+
+    image_desc = atk_image_get_image_description(aobject);
+    if (image_desc)
+      {
+        _print_key_value(tab_n, group_num, "Description", (gpointer)image_desc,
+          VALUE_STRING);
+      }
+    else
+      {
+        _print_key_value(tab_n, group_num, "Description", "NULL",
+          VALUE_STRING);
+      }
+
+    atk_image_get_image_position(aobject, &x, &y, ATK_XY_SCREEN);
+    atk_image_get_image_size(aobject, &height, &width);
+
+    output_str = g_strdup_printf("x: %d y: %d width: %d height %d",
+       x, y, width, height);
+    _print_key_value(tab_n, group_num, "Geometry", (gpointer)output_str,
+      VALUE_STRING);
+    g_free(output_str);
+    return(group_num);
+}
+
+static gint
+_print_selection (AtkSelection *aobject)
+{
+    AtkObject *object;
+    AtkRole role;
+    gchar *label_str, *output_str;
+    gint group_num;
+    gint n_selected, j, n_selectable;
+    TabNumber tab_n = SELECTION;
+
+    group_num = _print_groupname(tab_n, SELECTION_INTERFACE,
+      "Selection Interface");
+
+    n_selected = atk_selection_get_selection_count (aobject);
+    output_str = g_strdup_printf ("%d", n_selected);
+    _print_key_value (tab_n, group_num, "Number of Selected Children",
+                      (gpointer) output_str, VALUE_STRING);
+    g_free (output_str);
+    /*
+     * The number of selected items is the number of children except for
+     * a ComboBox where it is the number of items in the list.
+     */
+    object = ATK_OBJECT (aobject);
+    role = atk_object_get_role (object);
+    if (role == ATK_ROLE_COMBO_BOX)
+    {
+      object = atk_object_ref_accessible_child (object, 0);
+      g_return_val_if_fail (atk_object_get_role (object) == ATK_ROLE_LIST,
+                            group_num);
+      n_selectable = atk_object_get_n_accessible_children (object);
+      g_object_unref (G_OBJECT (object)); 
+    }
+    else
+    {
+      n_selectable = atk_object_get_n_accessible_children (object);
+    }
+    output_str = g_strdup_printf ("%d", n_selectable);
+    _print_key_value (tab_n, group_num, "Number of Selectable Children",
+                      (gpointer) output_str, VALUE_STRING);
+    g_free (output_str);
+
+    for (j = 0; j < n_selected; j++)
+    {
+      G_CONST_RETURN gchar *selected_name;
+      AtkObject *selected_object;
+
+      selected_object = atk_selection_ref_selection (aobject, j);
+      selected_name = atk_object_get_name (selected_object);
+      if (selected_name == NULL)
+      {
+        selected_name = "No name";
+      }
+      label_str = g_strdup_printf ("Selected item: %d Name", j+1);
+      _print_key_value (tab_n, group_num, label_str,
+                        (gpointer) selected_name, VALUE_STRING);
+      g_free (label_str);
+      g_object_unref (G_OBJECT (selected_object));
+    }
+    return group_num;
+}
+
+static gint
+_print_table (AtkTable *aobject)
+{
+    gchar *label_str, *output_str;
+    G_CONST_RETURN gchar *col_desc;
+    AtkObject *caption;
+    gint n_cols, n_rows;
+    gint i;
+    gint group_num;
+    TabNumber tab_n = TABLE;
+
+    group_num = _print_groupname(tab_n, TABLE_INTERFACE,
+      "Table Interface");
+
+    n_cols = atk_table_get_n_columns(aobject);
+    output_str = g_strdup_printf("%d", n_cols);
+    _print_key_value(tab_n, group_num, "Number Columns", (gpointer)output_str,
+      VALUE_STRING);
+    g_free(output_str);
+
+    n_rows = atk_table_get_n_rows(aobject);
+    output_str = g_strdup_printf("%d", n_rows);
+    _print_key_value(tab_n, group_num, "Number Rows", (gpointer)output_str,
+      VALUE_STRING);
+    g_free(output_str);
+
+    caption = atk_table_get_caption(aobject);
+    if (caption)
+      {
+        G_CONST_RETURN gchar* caption_name;
+
+        caption_name = atk_object_get_name (caption);
+        if (caption_name)
+          {
+            _print_key_value(tab_n, group_num, "Caption Name", 
+                             (gpointer)caption_name, VALUE_STRING);
+          }
+      }
+
+    for (i=0; i < n_cols; i++)
+      {
+        label_str = g_strdup_printf("Column %d Description", i + 1);
+
+        col_desc = atk_table_get_column_description(aobject, i);
+        if (col_desc)
+          {
+            _print_key_value(tab_n, group_num, label_str, (gpointer)col_desc,
+              VALUE_STRING);
+          }
+        else
+          {
+            _print_key_value(tab_n, group_num, label_str, "NULL",
+              VALUE_STRING);
+          }
+
+        g_free(label_str);
+      }
+
+    return(group_num);
+}
+
+static gint
+_print_text (AtkText *aobject)
+{
+    gchar *output_str, *text_val, *text_val_escaped;
+    gint n_chars, caret_offset;
+    gint start_offset, end_offset;
+    gint group_num;
+    gint x, y, w, h;
+    TabNumber tab_n = TEXT;
+
+    group_num = _print_groupname(tab_n, TEXT_INTERFACE,
+      "Text Content");
+
+    n_chars = atk_text_get_character_count(aobject);
+
+    output_str = g_strdup_printf("%d", n_chars);
+    _print_key_value(tab_n, group_num, "Total Character Count",
+      (gpointer)output_str, VALUE_STRING);
+    g_free(output_str);
+
+   /*
+    * Pass through g_strescape so that non-ASCII chars are made
+    * print-able.
+    */
+    text_val = atk_text_get_text (aobject, 0, n_chars);
+    if (text_val)
+      {
+        text_val_escaped = g_strescape(text_val, NULL);
+        _print_key_value (tab_n, group_num, "Text", (gpointer)text_val_escaped,
+          VALUE_TEXT);
+        g_free (text_val);
+        g_free (text_val_escaped);
+      }
+    else
+      {
+        _print_key_value (tab_n, group_num, "Text", "NULL", VALUE_TEXT);
+      }
+
+    caret_offset = atk_text_get_caret_offset(aobject);
+    output_str = g_strdup_printf("%d", caret_offset);
+    _print_key_value(tab_n, group_num, "Caret Offset", (gpointer)output_str,
+      VALUE_STRING);
+    g_free(output_str);
+
+    if (caret_offset < 0)
+      return(group_num);
+
+    text_val = atk_text_get_text_at_offset (aobject, caret_offset,
+                                            ATK_TEXT_BOUNDARY_CHAR,
+                                            &start_offset, &end_offset);
+    if (text_val)
+      {
+        text_val_escaped = g_strescape(text_val, NULL);
+        _print_key_value(tab_n, group_num, "Current Character",
+          (gpointer)text_val_escaped, VALUE_STRING);
+        g_free (text_val);
+        g_free (text_val_escaped);
+      }
+    else
+      {
+        _print_key_value(tab_n, group_num, "Current Character", "none",
+          VALUE_STRING);
+      }
+
+    atk_text_get_character_extents (aobject, caret_offset,
+                                    &x, &y, &w, &h, ATK_XY_SCREEN);
+    output_str = g_strdup_printf ("(%d, %d) (%d, %d)", x, y, w, h);
+    if (output_str)
+      {
+        _print_key_value(tab_n, group_num, "Character Bounds (screen)",
+          (gpointer)output_str, VALUE_STRING);
+        g_free(output_str);
+      }
+
+    atk_text_get_character_extents (aobject, caret_offset,
+                                    &x, &y, &w, &h, ATK_XY_WINDOW);
+    output_str = g_strdup_printf ("(%d, %d) (%d, %d)", x, y, w, h);
+    if (output_str)
+      {
+        _print_key_value(tab_n, group_num, "Character Bounds (window)",
+          (gpointer)output_str, VALUE_STRING);
+        g_free(output_str);
+      }
+
+    text_val = atk_text_get_text_at_offset (aobject, caret_offset,
+                                            ATK_TEXT_BOUNDARY_WORD_START,
+                                            &start_offset, &end_offset);
+    if (text_val)
+      {
+        text_val_escaped = g_strescape(text_val, NULL);
+        _print_key_value(tab_n, group_num, "Current Word",
+          (gpointer)text_val_escaped, VALUE_STRING);
+        g_free (text_val);
+        g_free (text_val_escaped);
+      }
+    else
+      {
+        _print_key_value(tab_n, group_num, "Current Word", "none",
+          VALUE_STRING);
+      }
+
+    text_val = atk_text_get_text_at_offset (aobject, caret_offset,
+                                            ATK_TEXT_BOUNDARY_LINE_START,
+                                            &start_offset, &end_offset);
+    if (text_val)
+      {
+        text_val_escaped = g_strescape(text_val, NULL);
+        _print_key_value(tab_n, group_num, "Current Line",
+          (gpointer)text_val_escaped, VALUE_STRING);
+        g_free (text_val);
+        g_free (text_val_escaped);
+      }
+    else
+      {
+        _print_key_value(tab_n, group_num, "Current Line", "none",
+          VALUE_STRING);
+      }
+
+    text_val = atk_text_get_text_at_offset (aobject, caret_offset,
+                                              ATK_TEXT_BOUNDARY_SENTENCE_START,
+                                              &start_offset, &end_offset);
+    if (text_val)
+      {
+        text_val_escaped = g_strescape(text_val, NULL);
+        _print_key_value(tab_n, group_num, "Current Sentence",
+          (gpointer)text_val_escaped, VALUE_STRING);
+        g_free (text_val);
+        g_free (text_val_escaped);
+      }
+    else
+      {
+        _print_key_value(tab_n, group_num, "Current Line", "none",
+          VALUE_STRING);
+      }
+    return(group_num);
+}
+
+static gint
+_print_text_attributes (AtkText *aobject)
+{
+    AtkAttributeSet *attribute_set;
+    AtkAttribute *attribute;
+    gchar *output_str, *label_str;
+    gint start_offset, end_offset, caret_offset;
+    gint attribute_set_len, attribute_offset, i;
+    gint n_chars;
+    gint group_num;
+    TabNumber tab_n = TEXT;
+
+    n_chars = atk_text_get_character_count(aobject);
+
+    group_num = _print_groupname (tab_n, TEXT_ATTRIBUTES,
+      "Text Attributes at Caret");
+
+    caret_offset = atk_text_get_caret_offset(aobject);
+    attribute_offset = caret_offset;
+
+    start_offset = 0;
+    end_offset = 0;
+
+    attribute_set = atk_text_get_run_attributes(aobject, attribute_offset,
+          &start_offset, &end_offset);
+
+    label_str = g_strdup_printf("Attribute run start");
+
+    output_str = g_strdup_printf("%d", start_offset);
+    _print_key_value(tab_n, group_num, label_str, (gpointer)output_str,
+                     VALUE_STRING);
+    g_free(label_str);
+    g_free(output_str);
+
+    label_str = g_strdup_printf("Attribute run end");
+    output_str = g_strdup_printf("%d", end_offset);
+    _print_key_value(tab_n, group_num, label_str, (gpointer)output_str,
+                     VALUE_STRING);
+    g_free(label_str);
+    g_free(output_str);
+
+    if (attribute_set == NULL)
+      attribute_set_len = 0;
+    else
+      attribute_set_len = g_slist_length(attribute_set);
+
+    label_str = g_strdup_printf("Number of Attributes");
+    output_str = g_strdup_printf("%d", attribute_set_len);
+    _print_key_value(tab_n, group_num, label_str, (gpointer)output_str,
+                     VALUE_STRING);
+    g_free(label_str);
+    g_free(output_str);
+
+    for (i=0; i < attribute_set_len; i++)
+      {
+        attribute = ((AtkAttribute *) g_slist_nth(attribute_set, i)->data);
+
+        _print_key_value(tab_n, group_num, attribute->name,
+                         (gpointer)attribute->value, VALUE_STRING);
+      }
+    if (attribute_set != NULL)
+      atk_attribute_set_free(attribute_set);
+
+
+    return(group_num);
+}
+
+static gint
+_print_value (AtkValue *aobject)
+{
+    GValue *value_back, val;
+    gint group_num;
+    TabNumber tab_n = VALUE;
+
+    value_back = &val;
+
+    group_num = _print_groupname(tab_n, VALUE_INTERFACE,
+      "Value Interface");
+
+    atk_value_get_current_value(aobject, value_back);
+    _print_value_type(group_num, "Value", value_back);
+    atk_value_get_minimum_value(aobject, value_back);
+    _print_value_type(group_num, "Minimum Value", value_back);
+    atk_value_get_maximum_value(aobject, value_back);
+    _print_value_type(group_num, "Maximum Value", value_back);
+    return(group_num);
+}
+
+static void
+_print_value_type(gint group_num, gchar *type, GValue *value)
+{
+    gchar *label_str = NULL;
+    gchar *output_str = NULL;
+    TabNumber tab_n = VALUE;
+
+    if (G_VALUE_HOLDS_DOUBLE (value))
+      {
+        label_str = g_strdup_printf("%s - Double", type);
+        output_str = g_strdup_printf("%f",
+          g_value_get_double (value));
+        _print_key_value(tab_n, group_num, label_str, (gpointer)output_str,
+          VALUE_STRING);
+      }
+    else if (G_VALUE_HOLDS_INT (value))
+      {
+        label_str = g_strdup_printf("%s - Integer", type);
+        output_str = g_strdup_printf("%d",
+          g_value_get_int (value));
+        _print_key_value(tab_n, group_num, label_str, (gpointer)output_str,
+          VALUE_STRING);
+      }
+    else
+      {
+        _print_key_value(tab_n, group_num, "Value", "Unknown Type",
+          VALUE_STRING);
+      }
+
+    if (label_str)
+        g_free(label_str);
+    if (output_str)
+        g_free(output_str);
+}
+
+static void
+_create_event_watcher (void)
+{
+    focus_tracker_id = atk_add_focus_tracker (_print_accessible);
+
+    if (track_mouse)
+      {
+        mouse_watcher_focus_id =
+          atk_add_global_event_listener(_mouse_watcher,
+          "Gtk:GtkWidget:enter_notify_event");
+        mouse_watcher_button_id =
+          atk_add_global_event_listener(_button_watcher,
+          "Gtk:GtkWidget:button_press_event");
+      }
+}
+
+static gboolean
+_mouse_watcher (GSignalInvocationHint *ihint,
+               guint                  n_param_values,
+               const GValue          *param_values,
+               gpointer               data)
+{
+    GObject *object;
+    GtkWidget *widget;
+
+    object = g_value_get_object (param_values + 0);
+
+    if (GTK_IS_MENU(object)) return TRUE;
+
+    g_assert (GTK_IS_WIDGET(object));
+
+    widget = GTK_WIDGET (object);
+    if (GTK_IS_WINDOW (widget))
+    {
+        GtkWidget *focus_widget = GTK_WINDOW (widget)->focus_widget;
+        if (focus_widget != NULL)
+            widget = focus_widget;
+    }
+
+    _print_accessible (gtk_widget_get_accessible (widget));
+    return TRUE;
+}
+
+static gboolean
+_button_watcher (GSignalInvocationHint *ihint,
+                 guint                  n_param_values,
+                 const GValue          *param_values,
+                 gpointer               data)
+{
+    GObject *object;
+    GtkWidget *widget;
+
+    object = g_value_get_object (param_values + 0);
+
+    widget = GTK_WIDGET (object);
+    if (GTK_IS_CONTAINER (widget))
+    {
+      if (G_VALUE_HOLDS_BOXED (param_values + 1))
+        {
+          GdkEventButton *event;
+          gpointer gp;
+          AtkObject *aobject;
+          AtkObject *child;
+          gint  aobject_x, aobject_y;
+          gint x, y;
+
+          gp = g_value_get_boxed (param_values + 1);
+          event = (GdkEventButton *) gp;
+          aobject = gtk_widget_get_accessible (widget);
+          aobject_x = aobject_y = 0;
+          atk_component_get_position (ATK_COMPONENT (aobject), 
+                                      &aobject_x, &aobject_y, 
+                                      ATK_XY_WINDOW);
+          x = aobject_x + (gint) event->x; 
+          y = aobject_y + (gint) event->y; 
+          child = atk_component_ref_accessible_at_point (ATK_COMPONENT (aobject),
+                                                         x,
+                                                         y,
+                                                         ATK_XY_WINDOW);
+          if (child)
+            {
+              _print_accessible (child);
+              g_object_unref (child);
+            }
+          else
+            {
+              if (!GTK_IS_MENU_ITEM (widget))
+                {
+                  g_print ("No child at position %d %d for %s\n", 
+                           x,
+                           y,
+                           g_type_name (G_OBJECT_TYPE (widget)));
+                }
+            }
+        }
+    }
+
+    return TRUE;
+}
+
+static void _add_notebook_page (GtkNotebook *nbook,
+                                GtkWidget *content_widget,
+                                GtkWidget **new_page,
+                                const gchar *label_text)
+{
+  GtkWidget *label;
+
+  if (content_widget != NULL)
+    {
+      *new_page = content_widget;
+    }
+  else
+    {
+      *new_page = gtk_vpaned_new ();
+    }
+
+  label = gtk_label_new ("");
+  gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), label_text);
+  gtk_notebook_append_page (notebook, *new_page, label);
+  gtk_widget_show(*new_page);
+}
+
+static void
+_create_notebook (void)
+{
+  TabInfo *tab;
+  notebook = GTK_NOTEBOOK (gtk_notebook_new());
+
+  tab = nbook_tabs[OBJECT];
+  _add_notebook_page (notebook, tab->main_box, &tab->page, "<b>_Object</b>");
+
+  tab = nbook_tabs[ACTION];
+  _add_notebook_page (notebook, tab->main_box, &tab->page, "<b>_Action</b>");
+
+  tab = nbook_tabs[COMPONENT];
+  _add_notebook_page (notebook, tab->main_box, &tab->page, "<b>_Component</b>");
+
+  tab = nbook_tabs[IMAGE];
+  _add_notebook_page (notebook, tab->main_box, &tab->page, "<b>_Image</b>");
+
+  tab = nbook_tabs[SELECTION];
+  _add_notebook_page (notebook, tab->main_box, &tab->page, "<b>_Selection</b>");
+
+  tab = nbook_tabs[TABLE];
+  _add_notebook_page (notebook, tab->main_box, &tab->page, "<b>_Table</b>");
+
+  tab = nbook_tabs[TEXT];
+  _add_notebook_page (notebook, tab->main_box, &tab->page, "<b>Te_xt</b>");
+
+  tab = nbook_tabs[VALUE];
+  _add_notebook_page (notebook, tab->main_box, &tab->page, "<b>_Value</b>");
+
+  g_signal_connect (GTK_OBJECT (notebook),
+                      "switch-page",
+                      GTK_SIGNAL_FUNC (_update_current_page),
+                      NULL);
+}
+
+static void
+_init_data(void)
+{
+  TabInfo *the_tab;
+
+  the_tab = g_new0(TabInfo, 1);
+  the_tab->page = NULL;
+  the_tab->main_box = gtk_vbox_new(FALSE, 20);
+  the_tab->name = "Object";
+  nbook_tabs[OBJECT] = the_tab;
+
+  the_tab = g_new0(TabInfo, 1);
+  the_tab->page = NULL;
+  the_tab->main_box = gtk_vbox_new(FALSE, 20);
+  the_tab->name = "Action";
+  nbook_tabs[ACTION] = the_tab;
+
+  the_tab = g_new0(TabInfo, 1);
+  the_tab->page = NULL;
+  the_tab->main_box = gtk_vbox_new(FALSE, 20);
+  the_tab->name = "Component";
+  nbook_tabs[COMPONENT] = the_tab;
+
+  the_tab = g_new0(TabInfo, 1);
+  the_tab->page = NULL;
+  the_tab->main_box = gtk_vbox_new(FALSE, 20);
+  the_tab->name = "Image";
+  nbook_tabs[IMAGE] = the_tab;
+
+  the_tab = g_new0(TabInfo, 1);
+  the_tab->page = NULL;
+  the_tab->main_box = gtk_vbox_new(FALSE, 20);
+  the_tab->name = "Selection";
+  nbook_tabs[SELECTION] = the_tab;
+
+  the_tab = g_new0(TabInfo, 1);
+  the_tab->page = NULL;
+  the_tab->main_box = gtk_vbox_new(FALSE, 20);
+  the_tab->name = "Table";
+  nbook_tabs[TABLE] = the_tab;
+
+  the_tab = g_new0(TabInfo, 1);
+  the_tab->page = NULL;
+  the_tab->main_box = gtk_vbox_new(FALSE, 20);
+  the_tab->name = "Text";
+  nbook_tabs[TEXT] = the_tab;
+
+  the_tab = g_new0(TabInfo, 1);
+  the_tab->page = NULL;
+  the_tab->main_box = gtk_vbox_new(FALSE, 20);
+  the_tab->name = "Value";
+  nbook_tabs[VALUE] = the_tab;
+}
+
+static void
+_create_window (void)
+{
+    static GtkWidget *window = NULL;
+
+    if (!window)
+    {
+        window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+        gtk_widget_set_name (window, "Ferret Window");
+        gtk_window_set_policy (GTK_WINDOW(window), TRUE, TRUE, FALSE);
+
+        g_signal_connect (GTK_OBJECT (window), "destroy",
+                           GTK_SIGNAL_FUNC (gtk_widget_destroyed),
+                           &window);
+
+        gtk_window_set_title (GTK_WINDOW (window), "GTK+ Ferret Output");
+        gtk_window_set_default_size (GTK_WINDOW (window), 333, 550);
+        gtk_container_set_border_width (GTK_CONTAINER (window), 0);
+
+        vbox1 = gtk_vbox_new (FALSE, 0);
+        gtk_container_add (GTK_CONTAINER (window), vbox1);
+        gtk_widget_show (vbox1);
+
+        menubar = gtk_menu_bar_new ();
+        gtk_box_pack_start (GTK_BOX (vbox1), menubar, FALSE, TRUE, 0);
+        gtk_widget_show (menubar);
+        menutop = gtk_menu_item_new_with_label("Menu");
+        gtk_menu_bar_append(GTK_MENU_BAR(menubar), menutop);
+        gtk_widget_show (menutop);
+        menu = gtk_menu_new();
+        gtk_menu_item_set_submenu (GTK_MENU_ITEM (menutop), menu);
+        gtk_widget_show (menu);
+
+        _add_menu(&menu, &menuitem_trackmouse, "Track Mouse", track_mouse,
+           GTK_SIGNAL_FUNC(_toggle_trackmouse));
+        _add_menu(&menu, &menuitem_trackfocus, "Track Focus", track_focus,
+           GTK_SIGNAL_FUNC(_toggle_trackfocus));
+        _add_menu(&menu, &menuitem_magnifier, "Magnifier", use_magnifier,
+           GTK_SIGNAL_FUNC(_toggle_magnifier));
+        _add_menu(&menu, &menuitem_festival, "Festival", use_festival,
+           GTK_SIGNAL_FUNC(_toggle_festival));
+        _add_menu(&menu, &menuitem_festival_terse, "Festival Terse",
+          (!say_role && !say_accel),
+          GTK_SIGNAL_FUNC(_toggle_festival_terse));
+        _add_menu(&menu, &menuitem_terminal, "Terminal Output", display_ascii,
+           GTK_SIGNAL_FUNC(_toggle_terminal));
+        _add_menu(&menu, &menuitem_no_signals, "No ATK Signals", no_signals,
+           GTK_SIGNAL_FUNC(_toggle_no_signals));
+
+        _create_notebook ();
+        gtk_container_add (GTK_CONTAINER (vbox1), GTK_WIDGET (notebook));
+        gtk_widget_show (GTK_WIDGET (notebook));
+    }
+    if (!GTK_WIDGET_VISIBLE (window))
+        gtk_widget_show (window);
+
+    mainWindow = GTK_WIDGET (window);
+}
+
+static void
+_add_menu(GtkWidget ** menu, GtkWidget ** menuitem, gchar * name,
+  gboolean init_value, GCallback func)
+{
+    *menuitem = gtk_check_menu_item_new_with_label(name);
+    gtk_check_menu_item_set_active(
+      GTK_CHECK_MENU_ITEM(*menuitem), init_value);
+    gtk_menu_shell_append (GTK_MENU_SHELL (*menu), *menuitem);
+    gtk_widget_show(*menuitem);
+    g_signal_connect(GTK_OBJECT(*menuitem), "toggled", func, NULL);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+    if (g_getenv ("FERRET_ASCII"))
+       display_ascii = TRUE;
+
+    if (g_getenv ("FERRET_NOSIGNALS"))
+       no_signals = TRUE;
+
+    if (display_ascii)
+       g_print("GTK ferret Module loaded\n");
+
+    if (g_getenv("FERRET_MAGNIFIER"))
+        use_magnifier = TRUE;
+
+    if (g_getenv ("FERRET_FESTIVAL"))
+        use_festival = TRUE;
+
+    if (g_getenv ("FERRET_MOUSETRACK"))
+        track_mouse = TRUE;
+
+    if (g_getenv ("FERRET_TERSE"))
+      {
+        say_role = FALSE;
+        say_accel = FALSE;
+      }
+
+    _init_data();
+
+    _create_window();
+
+    _create_event_watcher();
+
+    return 0;
+}
+
+static void
+_clear_tab(TabNumber tab_n)
+{
+    GList *group_list, *nv_list;
+    TabInfo *tab;
+    GroupInfo *group;
+    NameValue *nv;
+
+    tab = nbook_tabs[tab_n];
+
+    for (group_list = tab->groups; group_list; group_list = group_list->next)
+      {
+        group = (GroupInfo *) group_list->data;
+
+        if (group->is_scrolled)
+          gtk_widget_hide(GTK_WIDGET(group->scroll_outer_frame));
+
+        gtk_widget_hide(GTK_WIDGET(group->frame));
+        gtk_widget_hide(GTK_WIDGET(group->group_vbox));
+
+        for (nv_list = group->name_value; nv_list; nv_list = nv_list->next)
+          {
+            nv = (NameValue *) nv_list->data;
+            nv->active = FALSE;
+            gtk_widget_hide(GTK_WIDGET(nv->column1));
+            gtk_widget_hide(GTK_WIDGET(nv->column2));
+            gtk_widget_hide(GTK_WIDGET(nv->label));
+
+            switch (nv->type)
+              {
+              case VALUE_STRING:
+                gtk_widget_hide(GTK_WIDGET(nv->string));
+                break;
+              case VALUE_BOOLEAN:
+                gtk_widget_hide(GTK_WIDGET(nv->boolean));
+                break;
+              case VALUE_TEXT:
+                gtk_widget_hide(GTK_WIDGET(nv->text));
+                break;
+              case VALUE_BUTTON:
+                gtk_widget_hide(GTK_WIDGET(nv->button));
+                break;
+              }
+            gtk_widget_hide(GTK_WIDGET(nv->hbox));
+
+            /* Disconnect signal handler if any */
+            if (nv->signal_id != -1)
+              g_signal_handler_disconnect(nv->button, nv->signal_id);
+
+            nv->signal_id = -1;
+          }
+      }
+}
+
+static gint
+_print_groupname(TabNumber tab_n, GroupId group_id,
+  const char *groupname)
+{
+  TabInfo *tab;
+  GroupInfo *the_group;
+  gint rc = -1;
+
+  if (display_ascii)
+      g_print("\n<%s>\n", groupname);
+
+  tab = nbook_tabs[tab_n];
+  the_group = _get_group(tab, group_id, groupname);
+  rc = g_list_index(tab->groups, the_group);
+  return rc;
+}
+
+static GroupInfo*
+_get_group(TabInfo *tab, GroupId group_id, const gchar *groupname)
+{
+    GroupInfo *group = NULL;
+    gboolean found = FALSE;
+    GList *group_list;
+
+    for (group_list = tab->groups; group_list; group_list = group_list->next)
+      {
+        group = (GroupInfo *) group_list->data;
+        if (group_id == group->group_id)
+          {
+            found = TRUE;
+            break;
+          }
+      }
+
+   if (!found)
+     {
+       /* build a new one */
+       group = (GroupInfo *)g_new0(GroupInfo, 1);
+       group->group_id = group_id;
+       _get_group_scrolled(group);
+
+       if (group->is_scrolled)
+         {
+           group->frame = gtk_scrolled_window_new (NULL, NULL);
+           gtk_widget_set_usize(GTK_WIDGET(group->frame), -2,
+             group->default_height);
+           group->scroll_outer_frame = GTK_FRAME(gtk_frame_new(groupname));
+           gtk_container_add(GTK_CONTAINER(group->scroll_outer_frame),
+             group->frame);
+         }
+       else
+         {
+           group->frame = gtk_frame_new(groupname);
+         }
+
+       gtk_container_set_border_width(GTK_CONTAINER(group->frame), 10);
+
+       group->name = g_strdup(groupname);
+       group->group_vbox = GTK_VBOX(gtk_vbox_new(FALSE, 10));
+
+       if (group->is_scrolled)
+         {
+           gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (group->frame),
+              GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+           gtk_scrolled_window_add_with_viewport(
+             GTK_SCROLLED_WINDOW(group->frame),
+             GTK_WIDGET(group->group_vbox));
+         }
+       else
+         {
+           gtk_container_add(GTK_CONTAINER(group->frame),
+             GTK_WIDGET(group->group_vbox));
+         }
+
+       tab->groups = g_list_append (tab->groups, group);
+
+       if (group->is_scrolled)
+         {
+           gtk_box_pack_start_defaults(GTK_BOX(tab->main_box),
+              GTK_WIDGET(group->scroll_outer_frame));
+         }
+       else
+         {
+           gtk_box_pack_start_defaults(GTK_BOX(tab->main_box),
+             GTK_WIDGET(group->frame));
+         }
+     }
+
+   return group;
+}
+
+void
+_get_group_scrolled(GroupInfo *group)
+{
+   if (group->group_id == OBJECT_INTERFACE)
+     {
+       group->is_scrolled = FALSE;
+     }
+   else if (group->group_id == RELATION_INTERFACE)
+     {
+       group->is_scrolled = TRUE;
+       group->default_height = 50;
+     }
+   else if (group->group_id == STATE_INTERFACE)
+     {
+       group->is_scrolled = TRUE;
+       group->default_height = 100;
+     }
+   else if (group->group_id == ACTION_INTERFACE)
+     {
+       group->is_scrolled = TRUE;
+       group->default_height = 200;
+     }
+   else if (group->group_id == TEXT_ATTRIBUTES)
+     {
+       group->is_scrolled = TRUE;
+       group->default_height = 70;
+     }
+   else
+     {
+       group->is_scrolled = FALSE;
+     }
+}
+
+NameValue *
+_get_name_value(GroupInfo *group, const gchar *label,
+  gpointer value_ptr, ValueType type)
+{
+    NameValue *name_value = NULL;
+    GList *nv_list;
+    GValue *value;
+    gboolean found = FALSE;
+    static char *empty_string = "";
+
+    if (!label)
+      {
+        label = empty_string;
+      }
+
+    for (nv_list = group->name_value; nv_list; nv_list = nv_list->next)
+      {
+        name_value = (NameValue *) nv_list->data;
+        if (!name_value->active)
+          {
+            found = TRUE;
+            break;
+          }
+      }
+
+    if (!found)
+      {
+        name_value = (NameValue *)g_new0(NameValue, 1);
+        name_value->column1 = GTK_HBOX(gtk_hbox_new(FALSE, 10));
+        name_value->column2 = GTK_HBOX(gtk_hbox_new(FALSE, 10));
+        name_value->hbox = GTK_HBOX(gtk_hbox_new(FALSE, 5));
+        name_value->label = GTK_LABEL(gtk_label_new(label));
+        name_value->string = gtk_label_new (NULL);
+        name_value->boolean = gtk_check_button_new ();
+        name_value->text = gtk_entry_new_with_max_length (1000);
+        name_value->button = GTK_BUTTON(gtk_button_new ());
+
+        gtk_box_pack_end(GTK_BOX(name_value->column1),
+          GTK_WIDGET(name_value->label), FALSE, FALSE, 10);
+
+        switch (type)
+          {
+          case VALUE_STRING:
+            gtk_label_set_text(GTK_LABEL(name_value->string),
+              (gchar *) value_ptr);
+            gtk_box_pack_start(GTK_BOX(name_value->column2),
+              GTK_WIDGET(name_value->string), FALSE, FALSE, 10);
+            break;
+          case VALUE_BOOLEAN:
+            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(name_value->boolean),
+               *((gboolean *)value_ptr));
+            gtk_widget_set_sensitive(name_value->boolean, FALSE);
+            gtk_box_pack_start(GTK_BOX(name_value->column2),
+              GTK_WIDGET(name_value->boolean), FALSE, FALSE, 10);
+            break;
+          case VALUE_TEXT:
+            gtk_entry_set_text (GTK_ENTRY (name_value->text),
+              (gchar *)value_ptr);
+            gtk_box_pack_start(GTK_BOX(name_value->column2),
+              GTK_WIDGET(name_value->text), FALSE, FALSE, 10);
+          case VALUE_BUTTON:
+            value = &(name_value->button_gval);
+            memset (value, 0, sizeof (GValue));
+            g_value_init (value, G_TYPE_STRING);
+            g_value_set_string (value, (gchar *)value_ptr);
+            g_object_set_property(G_OBJECT(name_value->button),
+              "label", value);
+            gtk_box_pack_start(GTK_BOX(name_value->column2),
+              GTK_WIDGET(name_value->button), FALSE, FALSE, 10);
+            break;
+          }
+
+        gtk_box_pack_start_defaults(GTK_BOX(name_value->hbox),
+          GTK_WIDGET(name_value->column1));
+        gtk_box_pack_start_defaults(GTK_BOX(name_value->hbox),
+          GTK_WIDGET(name_value->column2));
+        gtk_container_add(GTK_CONTAINER(group->group_vbox),
+          GTK_WIDGET(name_value->hbox));
+        group->name_value = g_list_append (group->name_value, name_value);
+      }
+    else
+      {
+        gtk_label_set_text(GTK_LABEL(name_value->label), label);
+        switch (type)
+          {
+          case VALUE_STRING:
+            gtk_label_set_text(GTK_LABEL(name_value->string),
+              (gchar *) value_ptr);
+            break;
+          case VALUE_BOOLEAN:
+            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(name_value->boolean),
+               *((gboolean *)value_ptr));
+            gtk_widget_set_sensitive(name_value->boolean, FALSE);
+            break;
+          case VALUE_TEXT:
+            gtk_entry_set_text (GTK_ENTRY (name_value->text),
+              (gchar *) value_ptr);
+            break;
+          case VALUE_BUTTON:
+            value = &(name_value->button_gval);
+            memset (value, 0, sizeof (GValue));
+            g_value_init (value, G_TYPE_STRING);
+            g_value_set_string (value, (gchar *)value_ptr);
+            g_object_set_property(G_OBJECT(name_value->button),
+              "label", value);
+            break;
+          }
+      }
+
+    name_value->active = TRUE;
+    name_value->type = type;
+    name_value->signal_id = -1;
+
+    gtk_widget_show(GTK_WIDGET(name_value->label));
+
+    switch (type)
+      {
+      case VALUE_STRING:
+        gtk_widget_show(GTK_WIDGET(name_value->string));
+        break;
+      case VALUE_BOOLEAN:
+        gtk_widget_show(GTK_WIDGET(name_value->boolean));
+        break;
+      case VALUE_TEXT:
+        gtk_widget_show(GTK_WIDGET(name_value->text));
+        break;
+      case VALUE_BUTTON:
+        gtk_widget_show(GTK_WIDGET(name_value->button));
+        break;
+      }
+
+    gtk_widget_show(GTK_WIDGET(name_value->column1));
+    gtk_widget_show(GTK_WIDGET(name_value->column2));
+    gtk_widget_show(GTK_WIDGET(name_value->hbox));
+    gtk_widget_show(GTK_WIDGET(group->group_vbox));
+
+    return name_value;
+}
+
+static NameValue *
+_print_key_value(TabNumber tab_n, gint group_number,
+   const char *label, gpointer value, ValueType type)
+{
+  TabInfo *tab;
+  GroupInfo *the_group;
+
+  if (display_ascii)
+    {
+      if (type == VALUE_BOOLEAN)
+        {
+          if (*((gboolean *)value))
+              g_print("\t%-30s\tTRUE\n", label);
+          else
+              g_print("\t%-30s\tFALSE\n", label);
+        }
+      else
+        {
+          g_print("\t%-30s\t%s\n", label, 
+                  value ? (gchar *)value : "NULL");
+        }
+    }
+
+  tab = nbook_tabs[tab_n];
+  the_group = (GroupInfo *)g_list_nth_data(tab->groups, group_number);
+  return _get_name_value(the_group, label, (gpointer)value, type);
+}
+
+static void
+_finished_group(TabNumber tab_no, gint group_number)
+{
+    TabInfo *tab;
+    GroupInfo *the_group;
+
+    tab = nbook_tabs[tab_no];
+
+    the_group = (GroupInfo *)g_list_nth_data(tab->groups, group_number);
+
+    if (the_group->is_scrolled)
+      gtk_widget_show(GTK_WIDGET(the_group->scroll_outer_frame));
+
+    gtk_widget_show(GTK_WIDGET(the_group->frame));
+    gtk_widget_show(GTK_WIDGET(the_group->group_vbox));
+    gtk_widget_show(GTK_WIDGET(tab->main_box));
+}
+
+/* Signal handlers */
+
+static gulong child_added_id = 0;
+static gulong child_removed_id = 0;
+static gulong state_change_id = 0;
+
+static gulong text_caret_handler_id = 0;
+static gulong text_inserted_id = 0;
+static gulong text_deleted_id = 0;
+
+static gulong table_row_inserted_id = 0;
+static gulong table_column_inserted_id = 0;
+static gulong table_row_deleted_id = 0;
+static gulong table_column_deleted_id = 0;
+static gulong table_row_reordered_id = 0;
+static gulong table_column_reordered_id = 0;
+
+static gulong property_id = 0;
+
+static void
+_update_handlers(AtkObject *obj)
+{
+    /* Remove signal handlers from object that had focus before */
+
+    if (last_object != NULL && G_TYPE_CHECK_INSTANCE(last_object))
+      {
+        if (child_added_id != 0)
+           g_signal_handler_disconnect(last_object, child_added_id);
+        if (child_removed_id != 0)
+           g_signal_handler_disconnect(last_object, child_removed_id);
+        if (state_change_id != 0)
+           g_signal_handler_disconnect(last_object, state_change_id);
+
+        if (text_caret_handler_id != 0)
+           g_signal_handler_disconnect(last_object, text_caret_handler_id);
+        if (text_inserted_id != 0)
+           g_signal_handler_disconnect(last_object, text_inserted_id);
+        if (text_deleted_id != 0)
+           g_signal_handler_disconnect(last_object, text_deleted_id);
+
+        if (table_row_inserted_id != 0)
+           g_signal_handler_disconnect(last_object, table_row_inserted_id);
+        if (table_column_inserted_id != 0)
+           g_signal_handler_disconnect(last_object, table_column_inserted_id);
+        if (table_row_deleted_id != 0)
+           g_signal_handler_disconnect(last_object, table_row_deleted_id);
+        if (table_column_deleted_id != 0)
+           g_signal_handler_disconnect(last_object, table_column_deleted_id);
+        if (table_row_reordered_id != 0)
+           g_signal_handler_disconnect(last_object, table_row_reordered_id);
+        if (table_column_reordered_id != 0)
+           g_signal_handler_disconnect(last_object, table_column_reordered_id);
+        if (property_id != 0)
+           g_signal_handler_disconnect(last_object, property_id);
+
+        g_object_unref(last_object);
+      }
+
+    last_object = NULL;
+
+    child_added_id = 0;
+    child_removed_id = 0;
+    text_caret_handler_id = 0;
+    text_inserted_id = 0;
+    text_deleted_id = 0;
+    table_row_inserted_id = 0;
+    table_column_inserted_id = 0;
+    table_row_deleted_id = 0;
+    table_column_deleted_id = 0;
+    table_row_reordered_id = 0;
+    table_column_reordered_id = 0;
+    property_id = 0;
+
+    if (!G_TYPE_CHECK_INSTANCE(obj))
+        return;
+
+    g_object_ref(obj);
+    last_object = obj;
+
+    /* Add signal handlers to object that now has focus. */
+
+    if (ATK_IS_OBJECT(obj))
+      {
+         child_added_id = g_signal_connect_closure (obj,
+                "children_changed::add",
+                g_cclosure_new (G_CALLBACK (_notify_object_child_added),
+                NULL, NULL), FALSE);
+
+         child_removed_id = g_signal_connect_closure (obj,
+                "children_changed::remove",
+                g_cclosure_new (G_CALLBACK (_notify_object_child_removed),
+                NULL, NULL), FALSE);
+
+         state_change_id = g_signal_connect_closure (obj,
+                "state_change",
+                g_cclosure_new (G_CALLBACK (_notify_object_state_change),
+                NULL, NULL), FALSE);
+      }
+
+    if (ATK_IS_TEXT(obj))
+      {
+        text_caret_handler_id = g_signal_connect_closure_by_id (obj,
+                g_signal_lookup ("text_caret_moved", G_OBJECT_TYPE (obj)),
+                0, g_cclosure_new (G_CALLBACK (_notify_caret_handler),
+                NULL, NULL), FALSE);
+        text_inserted_id = g_signal_connect_closure (obj,
+                "text_changed::insert",
+                g_cclosure_new (G_CALLBACK (_notify_text_insert_handler),
+                NULL, NULL), FALSE);
+        text_deleted_id = g_signal_connect_closure (obj,
+                "text_changed::delete",
+                g_cclosure_new (G_CALLBACK (_notify_text_delete_handler),
+                NULL, NULL), FALSE);
+      }
+
+    if (ATK_IS_TABLE(obj))
+      {
+        table_row_inserted_id = g_signal_connect_closure_by_id (obj,
+                g_signal_lookup ("row_inserted", G_OBJECT_TYPE (obj)),
+                0, g_cclosure_new (G_CALLBACK (_notify_table_row_inserted),
+                NULL, NULL), FALSE);
+        table_column_inserted_id = g_signal_connect_closure_by_id (obj,
+                g_signal_lookup ("column_inserted", G_OBJECT_TYPE (obj)),
+                0, g_cclosure_new (G_CALLBACK (_notify_table_column_inserted),
+                NULL, NULL), FALSE);
+        table_row_deleted_id = g_signal_connect_closure_by_id (obj,
+                g_signal_lookup ("row_deleted", G_OBJECT_TYPE (obj)),
+                0, g_cclosure_new (G_CALLBACK (_notify_table_row_deleted),
+                NULL, NULL), FALSE);
+        table_column_deleted_id = g_signal_connect_closure_by_id (obj,
+                g_signal_lookup ("column_deleted", G_OBJECT_TYPE (obj)),
+                0, g_cclosure_new (G_CALLBACK (_notify_table_column_deleted),
+                NULL, NULL), FALSE);
+        table_row_reordered_id = g_signal_connect_closure_by_id (obj,
+                g_signal_lookup ("row_reordered", G_OBJECT_TYPE (obj)),
+                0, g_cclosure_new (G_CALLBACK (_notify_table_row_reordered),
+                NULL, NULL), FALSE);
+        table_column_reordered_id = g_signal_connect_closure_by_id (obj,
+                g_signal_lookup ("column_reordered", G_OBJECT_TYPE (obj)),
+                0, g_cclosure_new (G_CALLBACK (_notify_table_column_reordered),
+                NULL, NULL), FALSE);
+      }
+
+    property_id = g_signal_connect_closure_by_id (obj,
+      g_signal_lookup ("property_change", G_OBJECT_TYPE (obj)),
+      0, g_cclosure_new (G_CALLBACK (_property_change_handler),
+      NULL, NULL),
+          FALSE);
+}
+
+/* Text signals */
+
+static void
+_notify_text_insert_handler (GObject *obj, int position, int offset)
+{
+    gchar *text = atk_text_get_text (ATK_TEXT (obj), position, position + offset);
+    gchar *output_str = g_strdup_printf("position %d, length %d text: %s",
+      position, offset,  text ? text: "<NULL>");
+    _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TEXT,
+      "Text Insert", output_str);
+    g_free(output_str);
+}
+
+static void
+_notify_text_delete_handler (GObject *obj, int position, int offset)
+{
+    gchar *text = atk_text_get_text (ATK_TEXT (obj), position, position + offset);
+    gchar *output_str = g_strdup_printf("position %d, length %d text: %s",
+      position, offset,  text ? text: "<NULL>");
+    _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TEXT,
+      "Text Delete", output_str);
+    g_free(output_str);
+}
+
+static void
+_notify_caret_handler (GObject *obj, int position)
+{
+    gchar *output_str = g_strdup_printf("position %d", position);
+    _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TEXT,
+      "Text Caret Moved", output_str);
+    g_free(output_str);
+}
+
+/* Table signals */
+
+static void
+_notify_table_row_inserted (GObject *obj, gint start_offset,
+  gint length)
+{
+    gchar *output_str =
+      g_strdup_printf("position %d, num of rows inserted %d!\n",
+      start_offset, length);
+    _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
+      "Table Row Inserted", output_str);
+    g_free(output_str);
+}
+
+static void
+_notify_table_column_inserted (GObject *obj, gint start_offset,
+  gint length)
+{
+    gchar *output_str =
+      g_strdup_printf("position %d, num of rows inserted %d!\n",
+      start_offset, length);
+    _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
+      "Table Column Inserted", output_str);
+    g_free(output_str);
+}
+
+static void
+_notify_table_row_deleted (GObject *obj, gint start_offset,
+  gint length)
+{
+    gchar *output_str = g_strdup_printf("position %d, num of rows inserted %d!\n",
+      start_offset, length);
+    _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
+      "Table Row Deleted", output_str);
+    g_free(output_str);
+}
+
+static void
+_notify_table_column_deleted (GObject *obj, gint start_offset,
+  gint length)
+{
+    gchar *output_str = g_strdup_printf("position %d, num of rows inserted %d!\n",
+      start_offset, length);
+    _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
+      "Table Column Deleted", output_str);
+    g_free(output_str);
+}
+
+static void
+_notify_table_row_reordered (GObject *obj)
+{
+    _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
+      "Table Row Reordered", NULL);
+}
+
+static void
+_notify_table_column_reordered (GObject *obj)
+{
+    _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
+      "Table Column Reordered", NULL);
+}
+
+/* Object signals */
+
+static void
+_notify_object_child_added (GObject *obj, gint index,
+  AtkObject *child)
+{
+    gchar *output_str = g_strdup_printf("index %d", index);
+    _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_OBJECT,
+      "Child Added", output_str);
+    g_free(output_str);
+}
+
+static void
+_notify_object_child_removed (GObject *obj, gint index,
+  AtkObject *child)
+{
+    gchar *output_str = g_strdup_printf("index %d", index);
+    _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_OBJECT,
+      "Child Removed", output_str);
+    g_free(output_str);
+}
+
+static void 
+_notify_object_state_change (GObject *obj, gchar *name, gboolean set)
+{
+    gchar *output_str = g_strdup_printf("name %s %s set", 
+                        name, set ? "is" : "was");
+    _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_OBJECT,
+      "State Change", output_str);
+    g_free(output_str);
+}
+
+
+/* Function to print signals */
+
+static void
+_print_signal(AtkObject *aobject, FerretSignalType type,
+  const char *name, const char *info)
+{
+    TabNumber top_tab = gtk_notebook_get_current_page (notebook) + OBJECT;
+
+    if (no_signals)
+      return;
+
+    if (display_ascii)
+      {
+        if (info != NULL)
+            g_print("SIGNAL:\t%-34s\t%s\n", name, info);
+        else
+            g_print("SIGNAL:\t%-34s\n", name);
+      }
+
+    if (use_festival)
+      {
+        if ((type == FERRET_SIGNAL_TEXT) && (!strncmp(name, "Text Caret", 10)))
+          {
+            _speak_caret_event (aobject);
+         }
+        else if (type == FERRET_SIGNAL_TEXT)
+          {
+            last_caret_offset = atk_text_get_caret_offset (ATK_TEXT (aobject));
+          }
+      }
+
+    if (use_magnifier && ATK_IS_TEXT (aobject) &&
+        (type == FERRET_SIGNAL_TEXT) && (!strncmp(name, "Text Caret", 10)))
+      {
+        gint x, y, w, h;
+        gint caret_offset = atk_text_get_caret_offset (ATK_TEXT (aobject));
+        atk_text_get_character_extents ( ATK_TEXT (aobject), caret_offset, &x, &y, &w, &h, ATK_XY_SCREEN);
+        _send_to_magnifier (x, y, w, h);
+      }
+
+    if ((type == FERRET_SIGNAL_TEXT && top_tab == TEXT) ||
+        (type == FERRET_SIGNAL_TABLE && top_tab == TABLE) ||
+        (type == FERRET_SIGNAL_OBJECT && top_tab == OBJECT))
+      {
+        if (display_ascii)
+            g_print("Updating tab\n");
+
+        _update(top_tab, aobject);
+      }
+}
+
+/* Update functions */
+
+static void
+_update (TabNumber top_tab, AtkObject *aobject)
+{
+    gint group_num;
+
+    if (top_tab >= OBJECT && top_tab < END_TABS)
+    {
+       _clear_tab(top_tab);
+    }
+
+    if (top_tab == OBJECT && ATK_IS_OBJECT(aobject))
+      {
+        group_num = _print_object(aobject);
+        _finished_group(OBJECT, group_num);
+        group_num = _print_relation(aobject);
+        _finished_group(OBJECT, group_num);
+        group_num = _print_state(aobject);
+        _finished_group(OBJECT, group_num);
+      }
+    if (top_tab == TEXT && ATK_IS_TEXT(aobject))
+      {
+        group_num = _print_text(ATK_TEXT (aobject));
+        _finished_group(TEXT, group_num);
+        group_num = _print_text_attributes(ATK_TEXT (aobject));
+        _finished_group(TEXT, group_num);
+      }
+    if (top_tab == SELECTION && ATK_IS_SELECTION(aobject))
+      {
+        group_num = _print_selection(ATK_SELECTION (aobject));
+        _finished_group(SELECTION, group_num);
+      }
+    if (top_tab == TABLE && ATK_IS_TABLE(aobject))
+      {
+        group_num = _print_table(ATK_TABLE (aobject));
+        _finished_group(TABLE, group_num);
+      }
+    if (top_tab == ACTION && ATK_IS_ACTION(aobject))
+      {
+        group_num = _print_action(ATK_ACTION (aobject));
+        _finished_group(ACTION, group_num);
+      }
+    if (top_tab == COMPONENT && ATK_IS_COMPONENT(aobject))
+      {
+        group_num = _print_component(ATK_COMPONENT(aobject));
+        _finished_group(COMPONENT, group_num);
+      }
+    if (top_tab == IMAGE && ATK_IS_IMAGE(aobject))
+      {
+        group_num = _print_image(ATK_IMAGE (aobject));
+        _finished_group(IMAGE, group_num);
+      }
+    if (top_tab == VALUE && ATK_IS_VALUE(aobject))
+      {
+        group_num = _print_value(ATK_VALUE(aobject));
+        _finished_group(VALUE, group_num);
+      }
+}
+
+static void
+_update_current_page(GtkNotebook *notebook, gpointer p, guint current_page)
+{
+  _update(current_page+OBJECT, last_object);
+}
+
+/* Property listeners */
+
+static void _property_change_handler (AtkObject *obj,
+  AtkPropertyValues *values)
+{
+    TabNumber top_tab = gtk_notebook_get_current_page (notebook) + OBJECT;
+
+    if (no_signals)
+      return;
+
+   /*
+    * Only process if the property change corrisponds to the current
+    * object
+    */
+    if (obj != last_object)
+      {
+        if (display_ascii)
+          {
+            g_print("\nProperty change event <%s> for object not in focus\n",
+                values->property_name);
+          }
+
+        return;
+      }
+
+    if (display_ascii)
+      {
+        g_print("\nProperty change event <%s> occurred.\n",
+          values->property_name);
+      }
+
+   /*
+    * Update the top tab if a property changes.
+    *
+    * We may be able to ignore some property changes if they do not
+    * change anything in ferret.
+    */
+
+    if (top_tab == OBJECT &&
+       ((strcmp (values->property_name, "accessible-name") == 0) ||
+        (strcmp (values->property_name, "accessible-description") == 0) ||
+        (strcmp (values->property_name, "accessible-parent") == 0) ||
+        (strcmp (values->property_name, "accessible-value") == 0) ||
+        (strcmp (values->property_name, "accessible-role") == 0) ||
+        (strcmp (values->property_name, "accessible-component-layout") == 0) ||
+        (strcmp (values->property_name, "accessible-component-mdi-zorder") == 0) ||
+        (strcmp (values->property_name, "accessible-table-caption") == 0) ||
+        (strcmp (values->property_name,
+                 "accessible-table-column-description") == 0) ||
+        (strcmp (values->property_name,
+                 "accessible-table-column-header") == 0) ||
+        (strcmp (values->property_name,
+                 "accessible-table-row-description") == 0) ||
+        (strcmp (values->property_name,
+                 "accessible-table-row-header") == 0) ||
+        (strcmp (values->property_name, "accessible-table-summary") == 0)))
+      {
+        if (display_ascii)
+            g_print("Updating tab\n");
+
+        _update(top_tab, last_object);
+      }
+    else if (top_tab == VALUE &&
+        (strcmp (values->property_name, "accessible-value") == 0))
+      {
+        if (display_ascii)
+            g_print("Updating tab\n");
+
+        _update(top_tab, last_object);
+      }
+}
+
+/* Action button callback function */
+
+void _action_cb(GtkWidget *widget, gpointer  *userdata)
+{
+   NameValue *nv = (NameValue *)userdata;
+   atk_action_do_action(ATK_ACTION(nv->atkobj), nv->action_num);
+}
+
+/* Menu-bar callback functions */
+
+void _toggle_terminal(GtkCheckMenuItem *checkmenuitem,
+  gpointer user_data)
+{
+   if (checkmenuitem->active)
+       display_ascii = TRUE;
+   else
+       display_ascii = FALSE;
+}
+
+void _toggle_no_signals(GtkCheckMenuItem *checkmenuitem,
+  gpointer user_data)
+{
+   if (checkmenuitem->active)
+       no_signals = TRUE;
+   else
+       no_signals = FALSE;
+}
+
+void _toggle_magnifier(GtkCheckMenuItem *checkmenuitem,
+  gpointer user_data)
+{
+   if (checkmenuitem->active)
+       use_magnifier = TRUE;
+   else
+       use_magnifier = FALSE;
+}
+
+void _toggle_festival(GtkCheckMenuItem *checkmenuitem,
+  gpointer user_data)
+{
+   if (checkmenuitem->active)
+       use_festival = TRUE;
+   else
+       use_festival = FALSE;
+}
+
+void _toggle_festival_terse(GtkCheckMenuItem *checkmenuitem,
+  gpointer user_data)
+{
+   if (checkmenuitem->active)
+     {
+        say_role = FALSE;
+        say_accel = FALSE;
+     }
+   else
+     {
+        say_role = TRUE;
+        say_accel = TRUE;
+     }
+}
+
+void _toggle_trackmouse(GtkCheckMenuItem *checkmenuitem,
+  gpointer user_data)
+{
+   if (checkmenuitem->active)
+     {
+        mouse_watcher_focus_id =
+          atk_add_global_event_listener(_mouse_watcher,
+          "Gtk:GtkWidget:enter_notify_event");
+        mouse_watcher_button_id =
+          atk_add_global_event_listener(_button_watcher,
+          "Gtk:GtkWidget:button_press_event");
+       track_mouse = TRUE;
+     }
+   else
+     {
+       if (mouse_watcher_focus_id != -1)
+         {
+           atk_remove_global_event_listener(mouse_watcher_focus_id);
+           atk_remove_global_event_listener(mouse_watcher_button_id);
+           track_mouse = FALSE;
+         }
+     }
+}
+
+void _toggle_trackfocus(GtkCheckMenuItem *checkmenuitem,
+  gpointer user_data)
+{
+   if (checkmenuitem->active)
+     {
+       track_focus = TRUE;
+       focus_tracker_id = atk_add_focus_tracker (_print_accessible);
+     }
+   else
+     { 
+       g_print ("No longer tracking focus.\n");
+       track_focus = FALSE;
+       atk_remove_focus_tracker (focus_tracker_id);
+     }
+}
diff --git a/modules/other/gail/tests/testaction.c b/modules/other/gail/tests/testaction.c
new file mode 100644 (file)
index 0000000..9bed730
--- /dev/null
@@ -0,0 +1,87 @@
+#include <string.h>
+#include <stdlib.h>
+#include <gtk/gtk.h>
+#include "testlib.h"
+
+/*
+ * This module is used to test the implementation of AtkAction,
+ * i.e. the getting of the name and the getting and setting of description
+ */
+
+static void _create_event_watcher (void);
+static void _check_object (AtkObject *obj);
+
+static void 
+_check_object (AtkObject *obj)
+{
+  G_CONST_RETURN char *accessible_name;
+  G_CONST_RETURN gchar * typename = NULL;
+
+  if (GTK_IS_ACCESSIBLE (obj))
+  {
+    GtkWidget* widget = NULL;
+
+    widget = GTK_ACCESSIBLE (obj)->widget;
+    typename = g_type_name (GTK_OBJECT_TYPE (widget));
+    g_print ("Widget type name: %s\n", typename ? typename : "NULL");
+  }
+  typename = g_type_name (G_OBJECT_TYPE (obj));
+  g_print ("Accessible type name: %s\n", typename ? typename : "NULL");
+  accessible_name = atk_object_get_name (obj);
+  if (accessible_name)
+    g_print ("Name: %s\n", accessible_name);
+
+  if (ATK_IS_ACTION (obj))
+  {
+    AtkAction *action = ATK_ACTION (obj);
+    gint n_actions, i;
+    G_CONST_RETURN gchar *action_name;
+    G_CONST_RETURN gchar *action_desc;
+    G_CONST_RETURN gchar *action_binding;
+    const gchar *desc = "Test description";
+    n_actions = atk_action_get_n_actions (action);
+    g_print ("AtkAction supported number of actions: %d\n", n_actions);
+    for (i = 0; i < n_actions; i++)
+    {
+      action_name = atk_action_get_name (action, i);
+      g_print ("Name of Action %d: %s\n", i, action_name);
+      action_binding = atk_action_get_keybinding (action, i);
+      if (action_binding)
+        g_print ("Name of Action Keybinding %d: %s\n", i, action_binding);
+     
+      if (!atk_action_set_description (action, i, desc))
+      {
+        g_print ("atk_action_set_description failed\n");
+      }
+      else
+      {
+        action_desc = atk_action_get_description (action, i);
+        if (strcmp (desc, action_desc) != 0)
+        {
+          g_print ("Problem with setting and getting action description\n");
+        }
+      }
+    } 
+    if (atk_action_set_description (action, n_actions, desc))
+    {
+      g_print ("atk_action_set_description succeeded but should not have\n");
+    }
+  }
+}
+
+static void
+_create_event_watcher (void)
+{
+  atk_add_focus_tracker (_check_object);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+  g_print("testaction Module loaded\n");
+
+  _create_event_watcher();
+
+  return 0;
+}
diff --git a/modules/other/gail/tests/testbutton.c b/modules/other/gail/tests/testbutton.c
new file mode 100644 (file)
index 0000000..8f8ae47
--- /dev/null
@@ -0,0 +1,206 @@
+#include <gtk/gtk.h>
+#include "testlib.h"
+
+/*
+ * This module is used to test the accessible implementation for buttons
+ *
+ * 1) It verifies that ATK_STATE_ARMED is set when a button is pressed
+ * To check this click on the button whose name is specified in the
+ * environment variable TEST_ACCESSIBLE_NAME or "button box" if the
+ * environment variable is not set.
+ *
+ * 2) If the environment variable TEST_ACCESSIBLE_AUTO is set the program
+ * will execute the action defined for a GailButton once.
+ *
+ * 3) Change an inconsistent toggle button to be consistent and vice versa.
+ *
+ * Note that currently this code needs to be changed manually to test
+ * different actions.
+ */
+
+static void _create_event_watcher (void);
+static void _check_object (AtkObject *obj);
+static void button_pressed_handler (GtkButton *button);
+static void _print_states (AtkObject *obj);
+static void _print_button_image_info(AtkObject *obj);
+static gint _do_button_action (gpointer data);
+static gint _toggle_inconsistent (gpointer data);
+static gint _finish_button_action (gpointer data);
+
+#define NUM_VALID_ROLES 4
+
+static void 
+_check_object (AtkObject *obj)
+{
+  AtkRole role;
+  static gboolean first_time = TRUE;
+
+  role = atk_object_get_role (obj);
+  if (role == ATK_ROLE_FRAME)
+  /*
+   * Find the specified button in the window
+   */
+  {
+    AtkRole valid_roles[NUM_VALID_ROLES];
+    G_CONST_RETURN char *name;
+    AtkObject *atk_button;
+    GtkWidget *widget;
+
+    valid_roles[0] = ATK_ROLE_PUSH_BUTTON;
+    valid_roles[1] = ATK_ROLE_TOGGLE_BUTTON;
+    valid_roles[2] = ATK_ROLE_CHECK_BOX;
+    valid_roles[3] = ATK_ROLE_RADIO_BUTTON;
+
+    name = g_getenv ("TEST_ACCESSIBLE_NAME");
+    if (name == NULL)
+      name = "button box";
+    atk_button = find_object_by_accessible_name_and_role (obj, name,
+                     valid_roles, NUM_VALID_ROLES);
+
+    if (atk_button == NULL)
+    {
+      g_print ("Object not found for %s\n", name);
+      return;
+    }
+    g_assert (GTK_IS_ACCESSIBLE (atk_button));
+    widget = GTK_ACCESSIBLE (atk_button)->widget;
+    g_assert (GTK_IS_BUTTON (widget));
+    g_signal_connect (GTK_OBJECT (widget),
+                                    "pressed",
+                           GTK_SIGNAL_FUNC (button_pressed_handler),
+                                    NULL);
+    if (GTK_IS_TOGGLE_BUTTON (widget))
+    {
+      _toggle_inconsistent (GTK_TOGGLE_BUTTON (widget));
+    }
+    if (first_time)
+      first_time = FALSE;
+    else
+      return;
+
+    if (g_getenv ("TEST_ACCESSIBLE_AUTO"))
+    {
+      gtk_idle_add (_do_button_action, atk_button);
+    }
+  }
+}
+
+static gint _toggle_inconsistent (gpointer data)
+{
+  GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (data);
+
+  if (gtk_toggle_button_get_inconsistent (toggle_button))
+  {
+    gtk_toggle_button_set_inconsistent (toggle_button, FALSE);
+  }
+  else
+  {
+    gtk_toggle_button_set_inconsistent (toggle_button, TRUE);
+  }
+  return FALSE;
+} 
+
+static gint _do_button_action (gpointer data)
+{
+  AtkObject *obj = ATK_OBJECT (data);
+
+  atk_action_do_action (ATK_ACTION (obj), 2);
+
+  gtk_timeout_add (5000, _finish_button_action, obj);
+  return FALSE;
+}
+
+static gint _finish_button_action (gpointer data)
+{
+#if 0
+  AtkObject *obj = ATK_OBJECT (data);
+
+  atk_action_do_action (ATK_ACTION (obj), 0);
+#endif
+
+  return FALSE;
+}
+
+static void
+button_pressed_handler (GtkButton *button)
+{
+  AtkObject *obj;
+
+  obj = gtk_widget_get_accessible (GTK_WIDGET (button));
+  _print_states (obj);
+  _print_button_image_info (obj);
+    
+  if (GTK_IS_TOGGLE_BUTTON (button))
+  {
+    gtk_idle_add (_toggle_inconsistent, GTK_TOGGLE_BUTTON (button));
+  }
+}
+
+static void 
+_print_states (AtkObject *obj)
+{
+  AtkStateSet *state_set;
+  gint i;
+
+  state_set = atk_object_ref_state_set (obj);
+
+  g_print ("*** Start states ***\n");
+  for (i = 0; i < 64; i++)
+  {
+     AtkStateType one_state;
+     G_CONST_RETURN gchar *name;
+
+     if (atk_state_set_contains_state (state_set, i))
+     {
+       one_state = i;
+
+       name = atk_state_type_get_name (one_state);
+
+       if (name)
+         g_print("%s\n", name);
+     }
+  }
+  g_object_unref (state_set);
+  g_print ("*** End states ***\n");
+}
+
+static void 
+_print_button_image_info(AtkObject *obj) {
+
+  gint height, width;
+  G_CONST_RETURN gchar *desc;
+
+  height = width = 0;
+
+  if(!ATK_IS_IMAGE(obj)) 
+       return;
+
+  g_print("*** Start Button Image Info ***\n");
+  desc = atk_image_get_image_description(ATK_IMAGE(obj));
+  g_print ("atk_image_get_image_desc returns : %s\n", desc ? desc : "<NULL>");
+  atk_image_get_image_size(ATK_IMAGE(obj), &height ,&width);
+  g_print("atk_image_get_image_size returns: height %d width %d\n",height,width);
+  if(atk_image_set_image_description(ATK_IMAGE(obj), "New image Description")){
+       desc = atk_image_get_image_description(ATK_IMAGE(obj));
+       g_print ("atk_image_get_image_desc now returns : %s\n",desc ?desc:"<NULL>");
+  }
+  g_print("*** End Button Image Info ***\n");
+
+
+}
+
+static void
+_create_event_watcher (void)
+{
+  atk_add_focus_tracker (_check_object);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+  g_print("testbutton Module loaded\n");
+
+  _create_event_watcher();
+
+  return 0;
+}
diff --git a/modules/other/gail/tests/testcombo.c b/modules/other/gail/tests/testcombo.c
new file mode 100644 (file)
index 0000000..60f84a9
--- /dev/null
@@ -0,0 +1,189 @@
+#include <gtk/gtk.h>
+#include <testlib.h>
+
+static void _test_selection (AtkObject *obj);
+static void _check_combo_box (AtkObject *obj);
+static void _check_children (AtkObject *obj);
+static gint _open_combo_list (gpointer data);
+static gint _close_combo_list (gpointer data);
+
+#define NUM_VALID_ROLES 1
+
+static void _check_children (AtkObject *obj)
+{
+  gint n_children, i, j;
+  AtkObject *child;
+  AtkObject *grand_child;
+  AtkObject *parent;
+
+  n_children = atk_object_get_n_accessible_children (obj);
+
+  if (n_children > 1)
+  {
+    g_print ("*** Unexpected number of children for combo box: %d\n", 
+             n_children);
+    return;
+  }
+  if (n_children == 2)
+  {
+    child = atk_object_ref_accessible_child (obj, 1);
+    g_return_if_fail (atk_object_get_role (child) == ATK_ROLE_TEXT);
+    parent = atk_object_get_parent (child);
+    j = atk_object_get_index_in_parent (child);
+    if (j != 1)
+     g_print ("*** inconsistency between parent and children %d %d ***\n",
+              1, j);       
+    g_object_unref (G_OBJECT (child));
+  }
+
+  child = atk_object_ref_accessible_child (obj, 0);
+  g_return_if_fail (atk_object_get_role (child) == ATK_ROLE_LIST);
+  parent = atk_object_get_parent (child);
+  j = atk_object_get_index_in_parent (child);
+  if (j != 0)
+     g_print ("*** inconsistency between parent and children %d %d ***\n",
+              0, j);       
+
+  n_children = atk_object_get_n_accessible_children (child);
+  for (i = 0; i < n_children; i++)
+  {
+    G_CONST_RETURN gchar *name;
+
+    grand_child = atk_object_ref_accessible_child (child, i);
+    name = atk_object_get_name (grand_child);
+    g_print ("Index: %d Name: %s\n", i, name ? name : "<NULL>");
+    g_object_unref (G_OBJECT (grand_child));
+  }
+  g_object_unref (G_OBJECT (child));
+}
+  
+static void _test_selection (AtkObject *obj)
+{
+  gint count;
+  gint n_children;
+  AtkObject *list;
+
+  count = atk_selection_get_selection_count (ATK_SELECTION (obj));
+  g_return_if_fail (count == 0);
+
+  list = atk_object_ref_accessible_child (obj, 0);
+  n_children = atk_object_get_n_accessible_children (list); 
+  g_object_unref (G_OBJECT (list));
+
+  atk_selection_add_selection (ATK_SELECTION (obj), n_children - 1);
+  count = atk_selection_get_selection_count (ATK_SELECTION (obj));
+  g_return_if_fail (count == 1);
+  g_return_if_fail (atk_selection_is_child_selected (ATK_SELECTION (obj),
+                     n_children - 1)); 
+  atk_selection_add_selection (ATK_SELECTION (obj), 0);
+  count = atk_selection_get_selection_count (ATK_SELECTION (obj));
+  g_return_if_fail (count == 1);
+  g_return_if_fail (atk_selection_is_child_selected (ATK_SELECTION (obj), 0));
+  atk_selection_clear_selection (ATK_SELECTION (obj));
+  count = atk_selection_get_selection_count (ATK_SELECTION (obj));
+  g_return_if_fail (count == 0);
+}
+
+static void _check_combo_box (AtkObject *obj)
+{
+  static gboolean done = FALSE;
+  static gboolean done_selection = FALSE;
+  AtkRole role;
+
+  role = atk_object_get_role (obj);
+
+  if (role == ATK_ROLE_FRAME)
+  {
+    AtkRole roles[NUM_VALID_ROLES];
+    AtkObject *combo_obj;
+
+    if (done_selection)
+      return;
+
+    roles[0] = ATK_ROLE_COMBO_BOX;
+
+    combo_obj = find_object_by_role (obj, roles, NUM_VALID_ROLES);
+
+    if (combo_obj)
+    {
+      if (!done_selection)
+      {
+        done_selection = TRUE;
+      }
+      if (g_getenv ("TEST_ACCESSIBLE_COMBO_NOEDIT") != NULL)
+      {
+        GtkEntry *entry;
+
+        entry = GTK_ENTRY (GTK_COMBO (GTK_ACCESSIBLE (combo_obj)->widget)->entry);
+        gtk_entry_set_editable (entry, FALSE);
+      }
+      _check_children (combo_obj);
+      _test_selection (combo_obj);
+    }
+
+    return;
+  }
+  if (role != ATK_ROLE_COMBO_BOX)
+    return;
+
+  g_print ("*** Start ComboBox ***\n");
+  _check_children (obj);
+  if (!done)
+  {
+    gtk_idle_add (_open_combo_list, obj);
+    done = TRUE;
+  }
+  else
+      return;
+  g_print ("*** End ComboBox ***\n");
+}
+
+static gint _open_combo_list (gpointer data)
+{
+  AtkObject *obj = ATK_OBJECT (data);
+
+  g_print ("_open_combo_list\n");
+  atk_action_do_action (ATK_ACTION (obj), 0);
+
+  gtk_timeout_add (5000, _close_combo_list, obj);
+  return FALSE;
+}
+
+static gint _close_combo_list (gpointer data)
+{
+  AtkObject *obj = ATK_OBJECT (data);
+
+  gint count;
+  gint n_children;
+  AtkObject *list;
+
+  count = atk_selection_get_selection_count (ATK_SELECTION (obj));
+  g_return_val_if_fail (count == 0, FALSE);
+
+  list = atk_object_ref_accessible_child (obj, 0);
+  n_children = atk_object_get_n_accessible_children (list); 
+  g_object_unref (G_OBJECT (list));
+
+  atk_selection_add_selection (ATK_SELECTION (obj), n_children - 1);
+
+  atk_action_do_action (ATK_ACTION (obj), 0);
+
+  return FALSE;
+}
+
+static void
+_create_event_watcher (void)
+{
+  atk_add_focus_tracker (_check_combo_box);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+  g_print("testcombo Module loaded\n");
+
+  _create_event_watcher();
+
+  return 0;
+}
diff --git a/modules/other/gail/tests/testcomponent.c b/modules/other/gail/tests/testcomponent.c
new file mode 100644 (file)
index 0000000..7600c10
--- /dev/null
@@ -0,0 +1,121 @@
+#include <atk/atk.h>
+
+static void _check_position (AtkObject *obj);
+
+static void _check_position (AtkObject *obj)
+{
+  AtkObject *parent, *ret_object;
+
+  gint x, y, width, height;
+  gint x1, y1, width1, height1;
+
+  x = y = width = height = 0;
+  x1 = y1 = width1 = height1 = 0;
+
+  if (!ATK_IS_COMPONENT (obj))
+    return;
+
+  atk_component_get_extents (ATK_COMPONENT(obj), &x, &y, &width, &height, ATK_XY_SCREEN);
+  atk_component_get_position (ATK_COMPONENT(obj), &x1, &y1, ATK_XY_SCREEN );
+  atk_component_get_size (ATK_COMPONENT(obj), &width1, &height1);
+  if ((x1 !=  x) || (y1 != y))
+  {
+    g_print ("atk_component_get_extents and atk_get_position give different"
+             "  values: %d,%d %d,%d\n", x, y, x1, y1);
+  }
+  if ((width1 !=  width) || (height1 != height))
+  {
+    g_print ("atk_component_get_extents and atk_get_size give different"
+             "  values: %d,%d %d,%d\n", width, height, width1, height1);
+  }
+
+  atk_component_get_position (ATK_COMPONENT(obj), &x1, &y1, ATK_XY_SCREEN);
+  g_print ("Object Type: %s\n", g_type_name (G_OBJECT_TYPE (obj)));
+  g_print ("Object at %d, %d on screen\n", x1, y1);
+  g_print ("Object at %d, %d, size: %d, %d\n", x, y, width, height);
+
+  parent = atk_object_get_parent (obj);
+
+  if (ATK_IS_COMPONENT (parent))
+  {
+    gint px, py, pwidth, pheight;
+    
+    atk_component_get_extents (ATK_COMPONENT(parent),
+                               &px, &py, &pwidth, &pheight, ATK_XY_SCREEN);
+    g_print ("Parent Type: %s\n", g_type_name (G_OBJECT_TYPE (parent)));
+    g_print ("Parent at %d, %d, size: %d, %d\n", px, py, pwidth, pheight);
+    ret_object = atk_component_ref_accessible_at_point (ATK_COMPONENT (parent), 
+                                                        x, y, ATK_XY_SCREEN);
+
+    if (!ret_object)
+    {
+      g_print ("1:atk_component_ref_accessible_at_point returns NULL\n");
+    }
+    else if (ret_object != obj)
+    {
+      g_print ("1:atk_component_ref_accessible_at_point returns wrong value for %d %d\n",
+                x, y);
+      atk_component_get_extents (ATK_COMPONENT(ret_object),
+                                 &px, &py, &pwidth, &pheight, ATK_XY_SCREEN);
+      g_print ("ret_object at %d, %d, size: %d, %d\n", px, py, pwidth, pheight);
+    }
+    if (ret_object)
+      g_object_unref (G_OBJECT (ret_object));
+    ret_object = atk_component_ref_accessible_at_point (ATK_COMPONENT (parent), 
+                                                        x+width-1, y+height-1, ATK_XY_SCREEN);
+    if (!ret_object)
+    {
+      g_print ("2:atk_component_ref_accessible_at_point returns NULL\n");
+    }
+    else if (ret_object != obj)
+    {
+      g_print ("2:atk_component_ref_accessible_at_point returns wrong value for %d %d\n",
+                x+width-1, y+height-1);
+    } 
+    if (ret_object)
+      g_object_unref (G_OBJECT (ret_object));
+    ret_object = atk_component_ref_accessible_at_point (ATK_COMPONENT (parent), 
+                                                        x-1, y-1, ATK_XY_SCREEN);
+    if ((ret_object) && (ret_object == obj))
+    {
+      g_print ("3:atk_component_ref_accessible_at_point returns wrong value for %d %d\n",
+                x-1, y-1);
+    } 
+    if (ret_object)
+      g_object_unref (G_OBJECT (ret_object));
+    ret_object = atk_component_ref_accessible_at_point (ATK_COMPONENT (parent), 
+                                                        x+width, y+height, ATK_XY_SCREEN);
+    if ((ret_object) && (ret_object == obj))
+    {
+      g_print ("4:atk_component_ref_accessible_at_point returns wrong value for %d %d\n",
+                x+width, y+height);
+    }
+    if (ret_object)
+      g_object_unref (G_OBJECT (ret_object));
+  }
+  if (!atk_component_contains (ATK_COMPONENT(obj), x, y, ATK_XY_SCREEN))
+    g_print ("Component does not contain position, %d %d\n", x, y);
+  if (atk_component_contains (ATK_COMPONENT(obj), x-1, y-1, ATK_XY_SCREEN))
+    g_print ("Component does contain position, %d %d\n", x-1, y-1);
+  if (!atk_component_contains (ATK_COMPONENT(obj), x+width-1, y+height-1, ATK_XY_SCREEN))
+    g_print ("Component does not contain position, %d %d\n", 
+             x+width-1, y+height-1);
+  if (atk_component_contains (ATK_COMPONENT(obj), x+width, y+height, ATK_XY_SCREEN))
+    g_print ("Component does contain position, %d %d\n", x+width, y+height);
+}
+
+static void
+_create_event_watcher (void)
+{
+  atk_add_focus_tracker (_check_position);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+  g_print("testcomponent Module loaded\n");
+
+  _create_event_watcher();
+
+  return 0;
+}
diff --git a/modules/other/gail/tests/testimage.c b/modules/other/gail/tests/testimage.c
new file mode 100644 (file)
index 0000000..f8da1a8
--- /dev/null
@@ -0,0 +1,159 @@
+#include <gtk/gtk.h>
+#include "testlib.h"
+#include <stdlib.h>
+
+/*
+ * This test modules tests the AtkImage interface. When the module is loaded
+ * with testgtk , it also creates a dialog that contains GtkArrows and a 
+ * GtkImage. 
+ *
+ */
+
+typedef struct
+{
+  GtkWidget *dialog;
+  GtkWidget *arrow1;
+  GtkWidget *arrow2;
+  GtkWidget *arrow3;
+  GtkWidget *arrow4;
+  GtkWidget *close_button;
+  GtkImage  *image;
+}MainDialog;
+
+static void destroy (GtkWidget *widget, gpointer data)
+{
+  gtk_widget_destroy(GTK_WIDGET(data));
+}
+
+static void _check_arrows (AtkObject *obj)
+{
+  AtkRole role;
+  MainDialog *md;
+  static gint visibleDialog = 0;
+
+
+  role = atk_object_get_role(obj);
+  if(role == ATK_ROLE_FRAME) {
+
+       md = (MainDialog *) malloc (sizeof(MainDialog));
+       if (visibleDialog == 0)
+    {
+               md->arrow1 = gtk_arrow_new(GTK_ARROW_UP,GTK_SHADOW_IN);
+               md->arrow2 = gtk_arrow_new(GTK_ARROW_DOWN,GTK_SHADOW_IN);
+               md->arrow3 = gtk_arrow_new(GTK_ARROW_LEFT,GTK_SHADOW_OUT);
+               md->arrow4 = gtk_arrow_new(GTK_ARROW_RIGHT,GTK_SHADOW_OUT);
+               md->dialog = gtk_dialog_new();
+               gtk_window_set_modal(GTK_WINDOW(md->dialog), TRUE);
+        gtk_box_pack_start(GTK_BOX (GTK_DIALOG (md->dialog)->vbox),
+                                                        md->arrow1, TRUE,TRUE, 0);
+               gtk_box_pack_start(GTK_BOX (GTK_DIALOG (md->dialog)->vbox),
+                                                        md->arrow2, TRUE,TRUE, 0);
+               gtk_box_pack_start(GTK_BOX (GTK_DIALOG (md->dialog)->vbox),
+                                                        md->arrow3, TRUE,TRUE, 0);
+               gtk_box_pack_start(GTK_BOX (GTK_DIALOG (md->dialog)->vbox),
+                                                        md->arrow4, TRUE,TRUE, 0);
+               g_signal_connect(GTK_OBJECT(md->dialog), "destroy",
+                                                        GTK_SIGNAL_FUNC(destroy), md->dialog);
+
+           md->image = GTK_IMAGE(gtk_image_new_from_file("circles.xbm"));
+               gtk_box_pack_start(GTK_BOX (GTK_DIALOG (md->dialog)->vbox),
+                                                        GTK_WIDGET(md->image), TRUE,TRUE, 0);
+               md->close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+               g_signal_connect(GTK_OBJECT(md->close_button), "clicked",
+                                                        GTK_SIGNAL_FUNC(destroy), md->dialog);
+
+               gtk_box_pack_start(GTK_BOX (GTK_DIALOG (md->dialog)->action_area),
+                                                       md->close_button, TRUE,TRUE, 0);
+
+
+               gtk_widget_show_all(md->dialog);
+               visibleDialog = 1;
+
+       
+       }       
+ }
+}
+
+
+static void 
+_print_image_info(AtkObject *obj) {
+
+  gint height, width;
+  G_CONST_RETURN gchar *desc;
+  G_CONST_RETURN gchar *name = atk_object_get_name (obj);
+  G_CONST_RETURN gchar *type_name = g_type_name(G_TYPE_FROM_INSTANCE (obj));
+
+  height = width = 0;
+
+
+  if(!ATK_IS_IMAGE(obj)) 
+       return;
+
+  g_print("atk_object_get_name : %s\n", name ? name : "<NULL>");
+  g_print("atk_object_get_type_name : %s\n",type_name ?type_name :"<NULL>");
+  g_print("*** Start Image Info ***\n");
+  desc = atk_image_get_image_description(ATK_IMAGE(obj));
+  g_print ("atk_image_get_image_desc returns : %s\n",desc ? desc:"<NULL>");
+  atk_image_get_image_size(ATK_IMAGE(obj), &height ,&width);
+  g_print("atk_image_get_image_size returns: height %d width %d\n",
+                                                                                       height,width);
+  if(atk_image_set_image_description(ATK_IMAGE(obj),"New image Description")){
+       desc = atk_image_get_image_description(ATK_IMAGE(obj));
+       g_print ("atk_image_get_image_desc now returns : %s\n",desc?desc:"<NULL>");
+  }
+  g_print("*** End Image Info ***\n");
+
+
+}
+static void _traverse_children (AtkObject *obj)
+{
+  gint n_children, i;
+
+  n_children = atk_object_get_n_accessible_children (obj);
+  for (i = 0; i < n_children; i++)
+  {
+    AtkObject *child;
+
+    child = atk_object_ref_accessible_child (obj, i);
+       _print_image_info(child);
+    _traverse_children (child);
+    g_object_unref (G_OBJECT (child));
+  }
+}
+
+
+static void _check_objects (AtkObject *obj)
+{
+  AtkRole role;
+
+  g_print ("Start of _check_values\n");
+
+  _check_arrows(obj);
+  role = atk_object_get_role (obj);
+
+  if (role == ATK_ROLE_FRAME || role == ATK_ROLE_DIALOG)
+  {
+    /*
+     * Add handlers to all children.
+     */
+    _traverse_children (obj);
+  }
+  g_print ("End of _check_values\n");
+}
+
+
+static void
+_create_event_watcher (void)
+{
+  atk_add_focus_tracker (_check_objects);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+  g_print("testimages Module loaded\n");
+
+  _create_event_watcher();
+
+  return 0;
+}
diff --git a/modules/other/gail/tests/testlib.c b/modules/other/gail/tests/testlib.c
new file mode 100644 (file)
index 0000000..9b325d9
--- /dev/null
@@ -0,0 +1,954 @@
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "testlib.h" 
+
+static gint    _get_position_in_array          (gint           window,
+                                               gchar           *the_test_name);
+static gint    _get_position_in_parameters     (gint           window,
+                                               gchar           *label,
+                                               gint            position);
+static void    _create_output_window           (OutputWindow   **outwin);
+static gboolean        _create_select_tests_window     (AtkObject      *obj,
+                                                TLruntest      runtest,
+                                                OutputWindow   **outwin);
+static void    _toggle_selectedcb              (GtkWidget      *widget,
+                                               gpointer        test);
+static void    _testselectioncb                (GtkWidget      *widget,
+                                               gpointer        data);
+static void    _destroy                        (GtkWidget      *widget,
+                                               gpointer        data);
+
+/* General functions */
+
+/**
+ * find_object_by_role:
+ * @obj: An #AtkObject
+ * @roles: An array of roles to search for
+ * @num_roles: The number of entries in @roles
+ *
+ * Find the #AtkObject which is a decendant of the specified @obj
+ * which is of an #AtkRole type specified in the @roles array.
+ *
+ * Returns: the #AtkObject that meets the specified criteria or NULL
+ * if no object is found. 
+ **/
+AtkObject*
+find_object_by_role (AtkObject *obj,
+                     AtkRole   *roles,
+                     gint      num_roles)
+{
+  /*
+   * Find the first object which is a descendant of the specified object
+   * which matches the specified role.
+   *
+   * This function returns a reference to the AtkObject which should be
+   * removed when finished with the object.
+   */
+  gint i, j;
+  gint n_children;
+  AtkObject *child;
+
+  if (obj == NULL)
+    return NULL;
+
+  for (j=0; j < num_roles; j++)
+    {
+      if (atk_object_get_role (obj) == roles[j])
+        return obj;
+    }
+
+  n_children = atk_object_get_n_accessible_children (obj);
+  for (i = 0; i < n_children; i++)
+    {
+      AtkObject* found_obj;
+
+      child = atk_object_ref_accessible_child (obj, i);
+
+      if (child == NULL)
+        continue;
+
+      for (j=0; j < num_roles; j++)
+        {
+          if (atk_object_get_role (child) == roles[j])
+            return child;
+        }
+
+      found_obj = find_object_by_role (child, roles, num_roles);
+      g_object_unref (child);
+      if (found_obj)
+        return found_obj;
+    }
+  return NULL;
+}
+
+/**
+ * find_object_by_name_and_role:
+ * @obj: An #AtkObject
+ * @name: The GTK widget name
+ * @roles: An array of roles to search for
+ * @num_roles: The number of entries in @roles
+ *
+ * Find the #AtkObject which is a decendant of the specified @obj
+ * which is of an #AtkRole type specified in the @roles array which
+ * also has the GTK widget name specified in @name.
+ *
+ * Returns: the #AtkObject that meets the specified criteria or NULL
+ * if no object is found. 
+ **/
+AtkObject*
+find_object_by_name_and_role(AtkObject   *obj,
+                             const gchar *name,
+                             AtkRole    *roles,
+                             gint        num_roles)
+{
+  AtkObject *child;
+  GtkWidget* widget;
+  gint i, j;
+  gint n_children;
+
+  if (obj == NULL)
+    return NULL;
+
+  widget = GTK_ACCESSIBLE (obj)->widget;
+  if (GTK_IS_WIDGET (widget))
+    {
+      if (strcmp (name, gtk_widget_get_name(GTK_WIDGET (widget))) == 0)
+        {
+          for (j=0; j < num_roles; j++)
+            {
+              if (atk_object_get_role (obj) == roles[j])
+                return obj;
+            }
+        }
+    }
+
+  n_children = atk_object_get_n_accessible_children (obj);
+  for (i = 0; i < n_children; i++)
+    {
+      AtkObject* found_obj;
+      child = atk_object_ref_accessible_child (obj, i);
+
+      if (child == NULL)
+        continue;
+
+      widget = GTK_ACCESSIBLE (child)->widget;
+      if (GTK_IS_WIDGET (widget))
+        {
+          if (strcmp(name, gtk_widget_get_name(GTK_WIDGET (widget))) == 0)
+            {
+              for (j=0; j < num_roles; j++)
+                {
+                  if (atk_object_get_role (child) == roles[j])
+                    return child;
+                }
+            }
+        }
+      found_obj = find_object_by_name_and_role (child, name, roles, num_roles);
+      g_object_unref (child);
+      if (found_obj)
+        return found_obj;
+    }
+  return NULL;
+}
+
+/**
+ * find_object_by_accessible_name_and_role:
+ * @obj: An #AtkObject
+ * @name: The accessible name
+ * @roles: An array of roles to search for
+ * @num_roles: The number of entries in @roles
+ *
+ * Find the #AtkObject which is a decendant of the specified @obj
+ * which has the specified @name and matches one of the 
+ * specified @roles.
+ * 
+ * Returns: the #AtkObject that meets the specified criteria or NULL
+ * if no object is found. 
+ */
+AtkObject*
+find_object_by_accessible_name_and_role (AtkObject   *obj,
+                                         const gchar *name,
+                                         AtkRole     *roles,
+                                        gint        num_roles)
+{
+  AtkObject *child;
+  gint i, j;
+  gint n_children;
+  G_CONST_RETURN gchar *accessible_name;
+
+  if (obj == NULL)
+    return NULL;
+
+  accessible_name = atk_object_get_name (obj);
+  if (accessible_name && (strcmp(name, accessible_name) == 0))
+    {
+      for (j=0; j < num_roles; j++)
+        {
+          if (atk_object_get_role (obj) == roles[j])
+            return obj;
+        }
+    }
+
+  n_children = atk_object_get_n_accessible_children (obj);
+  for (i = 0; i < n_children; i++)
+    {
+      AtkObject* found_obj;
+
+      child = atk_object_ref_accessible_child (obj, i);
+
+      if (child == NULL)
+        continue;
+
+      accessible_name = atk_object_get_name (child);
+      if (accessible_name && (strcmp(name, accessible_name) == 0))
+        {
+          for (j=0; j < num_roles; j++)
+            {
+              if (atk_object_get_role (child) == roles[j])
+                return child;
+            }
+        }
+      found_obj = find_object_by_accessible_name_and_role (child, name, 
+                                                           roles, num_roles);
+      g_object_unref (child);
+      if (found_obj)
+        return found_obj;
+    }
+  return NULL;
+}
+
+/**
+ * find_object_by_name_and_role:
+ * @obj: An #AtkObject
+ * @type: The type 
+ *
+ * Find the #AtkObject which is a decendant of the specified @obj
+ * which has the specified @type.
+ * 
+ * Returns: the #AtkObject that meets the specified criteria or NULL
+ * if no object is found. 
+ */
+AtkObject*
+find_object_by_type (AtkObject *obj, 
+                     gchar     *type)
+{
+  /*
+   * Find the first object which is a descendant of the specified object
+   * which matches the specified type.
+   *
+   * This function returns a reference to the AtkObject which should be
+   * removed when finished with the object.
+   */
+  gint i;
+  gint n_children;
+  AtkObject *child;
+  G_CONST_RETURN gchar * typename = NULL;
+
+  if (obj == NULL)
+    return NULL;
+
+  typename = g_type_name (G_OBJECT_TYPE (obj));
+  if (strcmp (typename, type) == 0)
+     return obj;
+
+  n_children = atk_object_get_n_accessible_children (obj);
+  for (i = 0; i < n_children; i++)
+    {
+      AtkObject* found_obj;
+
+      child = atk_object_ref_accessible_child (obj, i);
+
+      if (child == NULL)
+        continue;
+
+      typename = g_type_name (G_OBJECT_TYPE (child));
+
+      if (strcmp (typename, type) == 0)
+        return child;
+
+      found_obj = find_object_by_type (child, type);
+      g_object_unref (child);
+      if (found_obj)
+        return found_obj;
+    }
+  return NULL;
+}
+
+/**
+ * already_accessed_atk_object
+ * @obj: An #AtkObject
+ *
+ * Keeps a static GPtrArray of objects that have been passed into this
+ * function. 
+ *
+ * Returns: TRUE if @obj has been passed into this function before
+ * and FALSE otherwise.
+ */
+gboolean
+already_accessed_atk_object (AtkObject *obj)
+{
+  static GPtrArray *obj_array = NULL;
+  gboolean found = FALSE;
+  gint i;
+
+  /*
+   * We create a property handler for each object if one was not associated
+   * with it already.
+   *
+   * We add it to our array of objects which have property handlers; if an
+   * object is destroyed it remains in the array.
+   */
+  if (obj_array == NULL)
+    obj_array = g_ptr_array_new ();
+
+  for (i = 0; i < obj_array->len; i++)
+    {
+      if (obj == g_ptr_array_index (obj_array, i))
+        {
+          found = TRUE;
+          break;
+        }
+    }
+  if (!found)
+    g_ptr_array_add (obj_array, obj);
+
+  return found;
+}
+
+/**
+ * display_children
+ * @obj: An #AtkObject
+ * @depth: Number of spaces to indent output.
+ * @child_number: The child number of this object.
+ *
+ * Displays the hierarchy of widgets starting from @obj.
+ **/
+void
+display_children (AtkObject *obj, 
+                  gint      depth, 
+                  gint      child_number)
+{
+  display_children_to_depth(obj, -1, depth, child_number);
+}
+
+/**
+ * display_children_to_depth
+ * @obj: An #AtkObject
+ * @to_depth: Display to this depth.
+ * @depth: Number of spaces to indent output.
+ * @child_number: The child number of this object.
+ *
+ * Displays the hierarchy of widgets starting from @obj only
+ * to the specified depth.
+ **/
+void
+display_children_to_depth (AtkObject *obj,
+                           gint      to_depth,
+                           gint      depth,
+                           gint      child_number)
+{
+  AtkRole role;
+  const gchar *rolename;
+  const gchar *typename;
+  gint n_children, parent_index, i;
+
+  if (to_depth >= 0 && depth > to_depth)
+     return;
+
+  if (obj == NULL)
+     return;
+
+  for (i=0; i < depth; i++)
+    g_print(" ");
+
+  role = atk_object_get_role (obj);
+  rolename = atk_role_get_name (role);
+
+ /*
+  * Note that child_number and parent_index should be the same
+  * unless there is an error.
+  */
+  parent_index = atk_object_get_index_in_parent(obj);
+  g_print("child <%d == %d> ", child_number, parent_index);
+
+  n_children = atk_object_get_n_accessible_children (obj);
+  g_print ("children <%d> ", n_children);
+
+  if (rolename)
+    g_print("role <%s>, ", rolename);
+  else
+    g_print("role <error>");
+
+  if (GTK_IS_ACCESSIBLE(obj))
+    {
+      GtkWidget *widget;
+
+      widget = GTK_ACCESSIBLE (obj)->widget;
+      g_print("name <%s>, ", gtk_widget_get_name(GTK_WIDGET (widget)));
+    }
+  else
+    g_print("name <NULL>, ");
+
+  typename = g_type_name (G_OBJECT_TYPE (obj));
+  g_print ("typename <%s>\n", typename);
+
+  for (i = 0; i < n_children; i++)
+    {
+      AtkObject *child;
+
+      child = atk_object_ref_accessible_child (obj, i);
+      if (child != NULL)
+        {
+          display_children_to_depth (child, to_depth, depth + 1, i);
+          g_object_unref (G_OBJECT (child));
+        }
+    }
+}
+
+/* Test GUI logic */
+
+/* GUI Information for the Select Tests Window */
+typedef struct
+{
+  GtkWidget     *selecttestsWindow;
+  GtkWidget     *hbox;
+  GtkWidget     *vbox;
+  GtkWidget     *label;
+  GtkWidget     *textInsert;
+  GtkWidget     *button;
+  gchar         *selecttestsTitle;
+}MainDialog;
+
+/* Functionality information about each added test */
+typedef struct
+{
+  GtkWidget     *toggleButton;
+  GtkWidget     *hbox;
+  GtkWidget     *parameterLabel[MAX_PARAMS];
+  GtkWidget     *parameterInput[MAX_PARAMS];
+  gchar         *testName;
+  gint          numParameters;
+}TestList;
+
+typedef struct
+{
+   TLruntest   runtest;
+   AtkObject*  obj;
+   gint               win_num;
+}TestCB;
+
+static MainDialog      *md[MAX_WINDOWS];
+static OutputWindow    *ow;
+
+/* An array containing function information on all of the tests */
+static TestList        listoftests[MAX_WINDOWS][MAX_TESTS];
+
+/* A counter for the actual number of added tests */
+gint                   counter;
+
+/* A global for keeping track of the window numbers */
+static gint           window_no = 0;
+/* An array containing the names of the tests that are "on" */
+static gchar           *onTests[MAX_WINDOWS][MAX_TESTS]; 
+static gint            g_visibleDialog = 0;
+static gint           testcount[MAX_WINDOWS];
+static TestCB          testcb[MAX_WINDOWS];
+
+/**
+ * create_windows:
+ * @obj: An #AtkObject
+ * @runtest: The callback function to run when the "Run Tests" button
+ *   is clicked.
+ * @outwin: The output window to use.  If NULL is passed in, then 
+ *   create a new one.
+ *
+ * Creates the test window and the output window (if @outwin is NULL)
+ * Runs _create_output_window() and _create_select_tests_window() 
+ * and sets g_visibleDialog to 1
+ *
+ * Returns: The window number of the created window if successful, -1 otherwise.
+ **/
+gint
+create_windows (AtkObject    *obj,
+                TLruntest    runtest,
+                OutputWindow **outwin)
+{
+  gboolean valid;  
+  gint tmp;
+
+  g_visibleDialog = 1;
+  _create_output_window(outwin); 
+  valid = _create_select_tests_window(obj, runtest, outwin);
+  if (valid)
+    {
+      tmp = window_no;
+      window_no++;
+      return tmp;
+    }
+  else
+    return -1;
+}
+
+/** 
+ * _create_output_window
+ * @outwin: If outwin is passed in as NULL, a new output window is created
+ *   otherwise, the outwin passed in is shared.
+ *
+ * Creates the Test Result Output Window .
+ **/
+static void
+_create_output_window (OutputWindow **outwin)
+{
+  GtkWidget *view;
+  GtkWidget *scrolled_window;
+  OutputWindow *localow;
+
+  if (*outwin == NULL)
+    {
+      localow = (OutputWindow *) malloc (sizeof(OutputWindow));
+   
+      localow->outputBuffer = gtk_text_buffer_new(NULL);
+      view = gtk_text_view_new_with_buffer(GTK_TEXT_BUFFER(localow->outputBuffer));
+      gtk_widget_set_usize(view, 700, 500);
+      gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD);
+      gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);  
+
+      localow->outputWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+      gtk_window_set_title(GTK_WINDOW(localow->outputWindow), "Test Output");
+      scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+
+      gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                     GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+      gtk_container_add(GTK_CONTAINER(localow->outputWindow), scrolled_window);
+      gtk_container_add(GTK_CONTAINER(scrolled_window), view);
+      gtk_text_buffer_get_iter_at_offset(localow->outputBuffer, &localow->outputIter, 0);
+      gtk_widget_show(view);
+      gtk_widget_show(scrolled_window);
+      gtk_widget_show(localow->outputWindow);
+
+      gtk_text_buffer_set_text(GTK_TEXT_BUFFER(localow->outputBuffer),
+        "\n\nWelcome to the test GUI:\nTest results are printed here\n\n", 58);
+      gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(localow->outputBuffer),
+                                          &localow->outputIter, 0);
+      *outwin = localow;
+      ow = *outwin;
+    }
+}
+
+/** 
+ * _create_select_tests_window:
+ * @obj: An #AtkObject
+ * @runtest: The callback function that is run when the "Run Tests"
+ *   button is clicked.
+ * @outwin: The output window to use.
+ *
+ * Creates the Test Select Window 
+ *
+ * Returns: TRUE if successful, FALSE otherwise
+ **/
+static gboolean
+_create_select_tests_window (AtkObject    *obj,
+                             TLruntest    runtest,
+                             OutputWindow **outwin)
+{
+  AtkText   *textwidget;
+  GtkWidget *hbuttonbox;
+  GtkWidget *scrolledWindow;
+
+  if (window_no >= 0 && window_no < MAX_WINDOWS)
+    {
+      md[window_no] = (MainDialog *) malloc (sizeof(MainDialog));
+     
+      textwidget = ATK_TEXT (obj);
+     
+      /* Setup Window */
+      md[window_no]->selecttestsTitle = "Test Setting";
+      md[window_no]->selecttestsWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+      gtk_window_set_title (GTK_WINDOW( ow->outputWindow),
+                            md[window_no]->selecttestsTitle);
+      gtk_window_set_resizable (GTK_WINDOW(md[window_no]->selecttestsWindow),
+                                FALSE);
+      gtk_window_set_position (GTK_WINDOW(md[window_no]->selecttestsWindow),
+                               GTK_WIN_POS_CENTER); 
+      g_signal_connect (GTK_OBJECT(md[window_no]->selecttestsWindow), 
+                          "destroy",
+                          GTK_SIGNAL_FUNC(_destroy), 
+                          &md[window_no]->selecttestsWindow);
+     
+      /* Setup Scrolling */
+      scrolledWindow = gtk_scrolled_window_new(NULL, NULL);
+      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledWindow),
+                                      GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); 
+      gtk_widget_set_usize (scrolledWindow, 500, 600);
+      gtk_container_add (GTK_CONTAINER (md[window_no]->selecttestsWindow), 
+                         scrolledWindow);
+      
+      /* Setup Layout */
+      md[window_no]->vbox = gtk_vbox_new (TRUE, 0);
+      md[window_no]->button = gtk_button_new_with_mnemonic ("_Run Tests");
+      hbuttonbox = gtk_hbutton_box_new ();
+      gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox),
+                                 GTK_BUTTONBOX_SPREAD);
+      gtk_box_pack_end_defaults (GTK_BOX (hbuttonbox), 
+                                 GTK_WIDGET (md[window_no]->button));
+      gtk_box_pack_end_defaults (GTK_BOX (md[window_no]->vbox), hbuttonbox);
+      gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolledWindow),
+                                             md[window_no]->vbox);
+
+      testcb[window_no].runtest = runtest;
+      testcb[window_no].obj = obj;
+      testcb[window_no].win_num = window_no; 
+      g_signal_connect (GTK_OBJECT (md[window_no]->button), 
+                          "clicked",
+                          GTK_SIGNAL_FUNC (_testselectioncb),  
+                          (gpointer)&testcb[window_no]);
+     
+      /* Show all */
+      gtk_widget_grab_focus (md[window_no]->button);
+      gtk_widget_show (md[window_no]->button);
+      gtk_widget_show (hbuttonbox); 
+      gtk_widget_show (scrolledWindow); 
+      gtk_widget_show_all (GTK_WIDGET (md[window_no]->selecttestsWindow));
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+/** 
+ * add_test
+ * @window: The window number
+ * @name: The test name
+ * @num_params: The number of arguments the test uses.
+ * @parameter_names: The names of each argument.
+ * @default_names: The default values of each argument.
+ *
+ * Adds a Test with the passed-in details to the Tests Select Window.  
+ *
+ * Returns: FALSE if the num_params passed in is greater than
+ * MAX_PARAMS, otherwise returns TRUE 
+ *
+ **/
+gboolean
+add_test (gint   window, 
+          gchar  *name,
+          gint   num_params,
+          gchar* parameter_names[],
+          gchar* default_names[])
+{
+  gint i;
+
+  if (num_params > MAX_PARAMS)
+    return FALSE;
+  else
+    {
+      md[window]->hbox = gtk_hbox_new (FALSE, 0);
+      gtk_box_set_spacing (GTK_BOX (md[window]->hbox), 10);
+      gtk_container_set_border_width (GTK_CONTAINER (md[window]->hbox), 10);
+      gtk_container_add (GTK_CONTAINER (md[window]->vbox), md[window]->hbox);
+      listoftests[window][testcount[window]].toggleButton =
+         gtk_toggle_button_new_with_label (name);
+      gtk_box_pack_start (GTK_BOX (md[window]->hbox),
+          listoftests[window][testcount[window]].toggleButton, FALSE, FALSE, 0);
+      listoftests[window][testcount[window]].testName = name;
+      listoftests[window][testcount[window]].numParameters = num_params;
+      for (i=0; i<num_params; i++) 
+        {
+         listoftests[window][testcount[window]].parameterLabel[i] =
+            gtk_label_new (parameter_names[i]);
+          gtk_box_pack_start (GTK_BOX (md[window]->hbox),
+          listoftests[window][testcount[window]].parameterLabel[i], FALSE, FALSE, 0);
+         listoftests[window][testcount[window]].parameterInput[i] = gtk_entry_new();
+          gtk_entry_set_text (GTK_ENTRY (listoftests[window][testcount[window]].parameterInput[i]),
+            default_names[i]);
+          gtk_widget_set_usize (listoftests[window][testcount[window]].parameterInput[i], 50, 22);
+         gtk_box_pack_start (GTK_BOX (md[window]->hbox),
+            listoftests[window][testcount[window]].parameterInput[i], FALSE, FALSE, 0);
+          gtk_widget_set_sensitive (
+            GTK_WIDGET (listoftests[window][testcount[window]].parameterLabel[i]), FALSE);
+          gtk_widget_set_sensitive (
+            GTK_WIDGET (listoftests[window][testcount[window]].parameterInput[i]), FALSE);
+         gtk_widget_show (listoftests[window][testcount[window]].parameterLabel[i]);
+         gtk_widget_show (listoftests[window][testcount[window]].parameterInput[i]);
+        }
+      g_signal_connect (GTK_OBJECT (listoftests[window][testcount[window]].toggleButton),
+                          "toggled", 
+                          GTK_SIGNAL_FUNC(_toggle_selectedcb),
+                          (gpointer)&(listoftests[window][testcount[window]]));
+      gtk_widget_show (listoftests[window][testcount[window]].toggleButton);
+      gtk_widget_show (md[window]->hbox);
+      gtk_widget_show (md[window]->vbox);
+
+      testcount[window]++;
+      counter++;
+      return TRUE;
+    }  
+}
+
+/** 
+ * tests_set:
+ * @window: The window number
+ * @count: Passes back the number of tests on.
+ *
+ * Gets an array of strings corresponding to the tests that are "on".
+ * A test is assumed on if the toggle button is on and if all its
+ * parameters have values.
+ *
+ * Returns: an array of strings corresponding to the tests that
+ * are "on".
+ **/
+gchar **tests_set(gint window, int *count)
+{
+  gint        i =0, j = 0, num;
+  gboolean    nullparam;
+  gchar*      input;
+
+  *count = 0;
+  for (i = 0; i < MAX_TESTS; i++)
+      onTests[window][i] = NULL;
+
+  for (i = 0; i < testcount[window]; i++)
+    {
+      nullparam = FALSE;
+      if (GTK_TOGGLE_BUTTON(listoftests[window][i].toggleButton)->active)
+        {
+          num = listoftests[window][i].numParameters;
+          for (j = 0; j < num; j++)
+            {
+              input = gtk_editable_get_chars (
+                    GTK_EDITABLE (listoftests[window][i].parameterInput[j]), 0, -1);
+
+              if (input != NULL && (! strcmp(input, "")))
+                nullparam = TRUE;
+            } 
+          if (!nullparam)
+            {
+              onTests[window][*count] = listoftests[window][i].testName;
+              *count = *count + 1; 
+            }
+        }
+    } 
+  return onTests[window];
+}
+
+/**
+ * _get_position_in_array:
+ * @window: The window number
+ * @the_test_name: The name of the test
+ *
+ * Gets the index of the passed-in @the_test_name.
+ *
+ * Returns: the position in listoftests[] of @the_test_name
+ **/
+static gint
+_get_position_in_array(gint  window,
+                       gchar *the_test_name)
+{
+  gint        i;
+  
+  for (i = 0; i < testcount[window]; i++)
+    {
+      if (strcmp(listoftests[window][i].testName, the_test_name) == 0)
+        return i;
+    }
+  return -1;
+}
+
+/**
+ * _get_position_in_parameters:
+ * @window: The window number
+ * @label: The label name
+ * @position: The parameter position
+ *
+ * Gets the index of the passed-in parameter @label.
+ *
+ * Returns: the position in parameterLabel[] (a member of
+ * listoftests[]) of @label 
+ **/
+static gint
+_get_position_in_parameters(gint  window,
+                            gchar *label,
+                            gint  position)
+{
+  gint                    i;
+  G_CONST_RETURN gchar    *label_string;
+  
+  for (i = 0; i < MAX_PARAMS; i++)
+    {
+      label_string = gtk_label_get_text( 
+               GTK_LABEL (listoftests[window][position].parameterLabel[i]));
+
+      if (strcmp(label_string, label) == 0)
+        return i;
+    }
+  return -1;
+}
+
+/** 
+ * set_output_buffer:
+ * @output: The string to add to the output buffer
+ * 
+ * Tidies up the output Window 
+ **/
+void
+set_output_buffer(gchar *output)
+{
+  gtk_text_buffer_insert (GTK_TEXT_BUFFER (ow->outputBuffer),
+                          &ow->outputIter, output, strlen(output));
+  gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (ow->outputBuffer),
+                                      &ow->outputIter, 0);
+}
+
+/**
+ * isVisibleDialog:
+ *
+ * Informs user if a visible test window running.
+ *
+ * Returns: TRUE if g_visibleDialog is set to 1, otherwise FALSE
+ **/
+gboolean
+isVisibleDialog(void)
+{
+ if (g_visibleDialog >= 1)
+   return TRUE;
+ else
+   return FALSE;
+}
+
+/**
+ * get_arg_of_func:
+ * @window: The window number
+ * @function_name: The name of the function
+ * @arg_label: The label of the argument.
+ *
+ * Gets the user input associated with the @function_name and @arg_label.
+ *
+ * Returns: the user input associated with the @function_name and @arg_label.
+ **/
+gchar*
+get_arg_of_func (gint  window,
+                 gchar *function_name,
+                 gchar *arg_label)
+{
+  G_CONST_RETURN gchar       *argString;
+  gchar                      *retString;
+  gint                       position, paramPosition;
+
+  position =  _get_position_in_array(window, function_name);
+
+  if (position == -1)
+    {
+      g_print("No such function\n");
+      return NULL;
+    }
+
+  paramPosition = _get_position_in_parameters(window, arg_label, position);
+
+  if (paramPosition == -1)
+    {
+      g_print("No such parameter Label\n");
+      return NULL;
+    }
+
+  if (position != -1 && paramPosition != -1)
+    {
+      argString = gtk_editable_get_chars (
+        GTK_EDITABLE (listoftests[window][position].parameterInput[paramPosition]),
+      0, -1);
+      retString = g_strdup(argString);
+    }
+  else
+    retString = NULL;
+
+  return retString;
+}
+
+/**
+ * string_to_int:
+ * @the_string: The string to convert
+ *
+ * Converts the passed-in string to an integer 
+ *
+ * Returns: An integer corresponding to @the_string.
+ **/
+int
+string_to_int (const char *the_string)
+{
+  char *end_ptr;
+  double ret_val;
+  int int_ret_val; 
+
+  while (1)
+    {
+      ret_val = strtod( the_string, &end_ptr);
+      if (*end_ptr == '\0')
+        break;
+      else
+        printf("\nError: input must be a number\n");
+    }
+   
+  int_ret_val = (int) ret_val;
+  return (int_ret_val);
+}
+
+/** 
+ * _toggle_selectedcb:
+ * @widget: The ToggleButton widget
+ * @test: user data containing the TestList structure.
+ *
+ * Toggle Button Callback, activating the text entry fields 
+ **/
+static void
+_toggle_selectedcb (GtkWidget *widget,
+                    gpointer  test)
+{
+  int i;
+  TestList *testlist = (TestList *) test;
+  gboolean toggled;
+  gboolean sensitive;
+  toggled = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+  if (toggled)
+    sensitive = TRUE;
+  else
+    sensitive = FALSE;
+
+  for (i=0; i < testlist->numParameters; i++)
+    {
+      gtk_widget_set_sensitive (GTK_WIDGET (testlist->parameterLabel[i]),
+                                sensitive);
+      gtk_widget_set_sensitive (GTK_WIDGET (testlist->parameterInput[i]),
+                                sensitive);
+    }
+}
+
+/* 
+ * _testselectioncb:
+ * widget: The Button widget
+ * data: The user data containing a TestCB structure
+ *
+ * Callback for when the "Run Tests" button is pressed 
+ **/
+static void
+_testselectioncb (GtkWidget *widget,
+                  gpointer data)
+{
+  TestCB* local_testcb = (TestCB *)data;
+  local_testcb->runtest(local_testcb->obj, local_testcb->win_num);
+}
+
+/**
+ * _destroy:
+ * @widget: The GUI widget
+ * @data: User data, not used.
+ *
+ * Destroy Callback.
+ **/
+static void
+_destroy (GtkWidget *widget,
+          gpointer  data)
+{
+  gtk_main_quit();
+}
+
diff --git a/modules/other/gail/tests/testlib.h b/modules/other/gail/tests/testlib.h
new file mode 100644 (file)
index 0000000..7118bbf
--- /dev/null
@@ -0,0 +1,70 @@
+#include <gtk/gtk.h>
+#include <stdio.h>
+
+/* Maximum characters in the output buffer */
+#define MAX_LINE_SIZE   1000
+
+/* Maximum number of tests */
+#define MAX_TESTS       30 
+
+/* Maximum number of test windows */
+#define MAX_WINDOWS    5
+
+/* Maximum number of parameters any test can have */
+#define MAX_PARAMS      3
+
+/* Information on the Output Window */
+
+typedef struct
+{
+  GtkWidget     *outputWindow;
+  GtkTextBuffer *outputBuffer; 
+  GtkTextIter   outputIter;
+}OutputWindow;
+
+typedef void (*TLruntest) (AtkObject * obj, gint win_num);
+
+/* General purpose functions */
+
+gboolean               already_accessed_atk_object     (AtkObject      *obj);
+AtkObject*             find_object_by_role             (AtkObject      *obj,
+                                                       AtkRole         *role,
+                                                       gint            num_roles);
+AtkObject*             find_object_by_type             (AtkObject      *obj,
+                                                       gchar           *type);
+AtkObject*             find_object_by_name_and_role    (AtkObject      *obj,
+                                                       const gchar     *name,
+                                                       AtkRole         *roles,
+                                                       gint            num_roles);
+AtkObject*             find_object_by_accessible_name_and_role (AtkObject *obj,
+                                                       const gchar     *name,
+                                                       AtkRole         *roles,
+                                                       gint            num_roles);
+void                   display_children                (AtkObject      *obj,
+                                                        gint           depth,
+                                                        gint           child_number);
+void                   display_children_to_depth       (AtkObject      *obj,
+                                                        gint           to_depth,
+                                                        gint           depth,
+                                                        gint           child_number);
+
+
+/* Test GUI functions */
+
+gint                   create_windows                  (AtkObject      *obj,
+                                                       TLruntest       runtest,
+                                                       OutputWindow    **outwin);
+gboolean               add_test                        (gint           window,
+                                                       gchar           *name,
+                                                       gint            num_params,
+                                                       gchar           *parameter_names[],
+                                                       gchar           *default_names[]);
+void                   set_output_buffer               (gchar          *output);
+gchar                  **tests_set                     (gint           window,
+                                                       int             *count);
+gchar                  *get_arg_of_func                (gint           window,
+                                                       gchar           *function_name,
+                                                       gchar           *arg_label);
+int                    string_to_int                   (const char     *the_string);
+gboolean               isVisibleDialog                 (void);
+
diff --git a/modules/other/gail/tests/testmenuitem.c b/modules/other/gail/tests/testmenuitem.c
new file mode 100644 (file)
index 0000000..129bde5
--- /dev/null
@@ -0,0 +1,138 @@
+#include <string.h>
+#include <gtk/gtk.h>
+#include "testlib.h"
+
+/*
+ * This module is used to test the accessible implementation for menu items
+ *
+ * 1) When a menu item is clicked in testgtk, the action for the
+ * item is performed.
+ * 2) The name of the keybinding for the 'activate" action for a menu item
+ * is output, if it exists.
+ * 3) Execute the action for a menu item programatically
+ */
+#define NUM_VALID_ROLES 1
+
+static void _create_event_watcher (void);
+static void _check_object (AtkObject *obj);
+static gint _do_menu_item_action (gpointer data);
+
+static void 
+_check_object (AtkObject *obj)
+{
+  AtkRole role;
+  static G_CONST_RETURN char *name = NULL;
+  static gboolean first_time = TRUE;
+
+  role = atk_object_get_role (obj);
+  if (role == ATK_ROLE_FRAME)
+  /*
+   * Find the specified menu item
+   */
+  {
+    AtkRole valid_roles[NUM_VALID_ROLES];
+    AtkObject *atk_menu_item;
+    GtkWidget *widget;
+
+    if (name == NULL)
+    {
+      valid_roles[0] = ATK_ROLE_MENU_ITEM;
+
+      name = g_getenv ("TEST_ACCESSIBLE_NAME");
+      if (name == NULL)
+        name = "foo";
+    }
+    atk_menu_item = find_object_by_accessible_name_and_role (obj, name,
+                     valid_roles, NUM_VALID_ROLES);
+
+    if (atk_menu_item == NULL)
+    {
+      g_print ("Object not found for %s\n", name);
+      return;
+    }
+
+    g_assert (GTK_IS_ACCESSIBLE (atk_menu_item));
+    widget = GTK_ACCESSIBLE (atk_menu_item)->widget;
+    g_assert (GTK_IS_MENU_ITEM (widget));
+
+    if (first_time)
+      first_time = FALSE;
+    else
+      return;
+
+    /*
+     * This action opens the menu whose name is "foo" or whatever
+     * was specified in the environment variable TEST_ACCESSIBLE_NAME
+     */
+    atk_action_do_action (ATK_ACTION (atk_menu_item), 0);
+  }
+  else if ((role == ATK_ROLE_MENU_ITEM) ||
+           (role == ATK_ROLE_CHECK_MENU_ITEM) ||
+           (role == ATK_ROLE_RADIO_MENU_ITEM) ||
+           (role == ATK_ROLE_TEAR_OFF_MENU_ITEM))
+  {
+    G_CONST_RETURN char *keybinding;
+    G_CONST_RETURN char *accessible_name;
+
+    accessible_name = atk_object_get_name (obj);
+    if (accessible_name)
+      g_print ("Name: %s\n", accessible_name);
+    g_print ("Action: %s\n", atk_action_get_name (ATK_ACTION (obj), 0));
+    keybinding = atk_action_get_keybinding (ATK_ACTION (obj), 0);
+    if (keybinding)
+      g_print ("KeyBinding: %s\n", keybinding);
+    /*
+     * Do the action associated with the menu item once, otherwise
+     * we get into a loop
+     */
+    if (strcmp (name, accessible_name) == 0)
+    {
+      if (first_time)
+        first_time = FALSE;
+      else
+        return;
+      if (g_getenv ("TEST_ACCESSIBLE_AUTO"))
+      {
+        gtk_idle_add (_do_menu_item_action, obj);
+      }
+    }
+  }
+  else
+  {
+    G_CONST_RETURN char *accessible_name;
+
+    accessible_name = atk_object_get_name (obj);
+    if (accessible_name)
+      g_print ("Name: %s\n", accessible_name);
+    else if (GTK_IS_ACCESSIBLE (obj))
+    {
+      GtkWidget *widget = GTK_ACCESSIBLE (obj)->widget;
+      g_print ("Type: %s\n", g_type_name (G_OBJECT_TYPE (widget)));
+    } 
+  }
+}
+
+static gint _do_menu_item_action (gpointer data)
+{
+  AtkObject *obj = ATK_OBJECT (data);
+
+  atk_action_do_action (ATK_ACTION (obj), 0);
+
+  return FALSE;
+}
+
+static void
+_create_event_watcher (void)
+{
+  atk_add_focus_tracker (_check_object);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+  g_print("testmenuitem Module loaded\n");
+
+  _create_event_watcher();
+
+  return 0;
+}
diff --git a/modules/other/gail/tests/testnotebook.c b/modules/other/gail/tests/testnotebook.c
new file mode 100644 (file)
index 0000000..77d015b
--- /dev/null
@@ -0,0 +1,226 @@
+#include <stdio.h>
+
+#include <glib.h>
+#include <atk/atk.h>
+#include <atk/atkselection.h>
+#include <gtk/gtk.h>
+#include "testlib.h"
+
+#define NUM_VALID_ROLES 1
+
+static void _print_type (AtkObject *obj);
+static void _do_selection (AtkObject *obj);
+static gint _finish_selection (gpointer data);
+static gint _remove_page (gpointer data);
+
+static void _print_type (AtkObject *obj)
+{
+  G_CONST_RETURN gchar *typename = NULL;
+  G_CONST_RETURN gchar *name = NULL;
+  G_CONST_RETURN gchar *description = NULL;
+  AtkRole role;
+
+  if (GTK_IS_ACCESSIBLE (obj))
+  {
+    GtkWidget* widget = NULL;
+
+    widget = GTK_ACCESSIBLE (obj)->widget;
+    typename = g_type_name (GTK_OBJECT_TYPE (widget));
+    g_print ("\tWidget type name: %s\n", typename ? typename : "NULL");
+  }
+
+  typename = g_type_name (G_OBJECT_TYPE (obj));
+  g_print ("\tAccessible type name: %s\n", typename ? typename : "NULL");
+  
+  name = atk_object_get_name (obj);
+  g_print("\tAccessible Name: %s\n", (name) ? name : "NULL");
+  
+  role = atk_object_get_role(obj);
+  g_print ("\tAccessible Role: %d\n", role);
+  
+  description = atk_object_get_description (obj);
+  g_print ("\tAccessible Description: %s\n", (description) ? description : "NULL");
+  if (role ==  ATK_ROLE_PAGE_TAB)
+  {
+    AtkObject *parent, *child;
+    gint x, y, width, height;
+
+    x = y = width = height = 0;
+    atk_component_get_extents (ATK_COMPONENT (obj), &x, &y, &width, &height,
+                               ATK_XY_SCREEN);
+    g_print ("obj: x: %d y: %d width: %d height: %d\n", x, y, width, height);
+    x = y = width = height = 0;
+    atk_component_get_extents (ATK_COMPONENT (obj), &x, &y, &width, &height,
+                               ATK_XY_WINDOW);
+    g_print ("obj: x: %d y: %d width: %d height: %d\n", x, y, width, height);
+    parent = atk_object_get_parent (obj);
+    x = y = width = height = 0;
+    atk_component_get_extents (ATK_COMPONENT (parent), &x, &y, &width, &height,
+                               ATK_XY_SCREEN);
+    g_print ("parent: x: %d y: %d width: %d height: %d\n", x, y, width, height);
+    x = y = width = height = 0;
+    atk_component_get_extents (ATK_COMPONENT (parent), &x, &y, &width, &height,
+                               ATK_XY_WINDOW);
+    g_print ("parent: x: %d y: %d width: %d height: %d\n", x, y, width, height);
+
+    child = atk_object_ref_accessible_child (obj, 0);
+    x = y = width = height = 0;
+    atk_component_get_extents (ATK_COMPONENT (child), &x, &y, &width, &height,
+                               ATK_XY_SCREEN);
+    g_print ("child: x: %d y: %d width: %d height: %d\n", x, y, width, height);
+    x = y = width = height = 0;
+    atk_component_get_extents (ATK_COMPONENT (child), &x, &y, &width, &height,
+                               ATK_XY_WINDOW);
+    g_print ("child: x: %d y: %d width: %d height: %d\n", x, y, width, height);
+
+    g_object_unref (child);
+  }
+}
+
+
+static void
+_do_selection (AtkObject *obj)
+{
+  gint i;
+  gint n_children;
+  AtkRole role;
+  AtkObject *selection_obj;
+  static gboolean done_selection = FALSE;
+  
+  if (done_selection)
+    return;
+
+  role = atk_object_get_role (obj);
+
+  if (role == ATK_ROLE_FRAME)
+  {
+    AtkRole roles[NUM_VALID_ROLES];
+
+    roles[0] = ATK_ROLE_PAGE_TAB_LIST;
+
+    selection_obj = find_object_by_role (obj, roles, NUM_VALID_ROLES);
+
+    if (selection_obj)
+    {
+      done_selection = TRUE;
+    }
+    else
+      return;
+  }
+  else
+  {
+    return;
+  }
+
+  g_print ("*** Start do_selection ***\n");
+  
+  n_children = atk_object_get_n_accessible_children (selection_obj);
+  g_print ("*** Number of notebook pages: %d\n", n_children); 
+
+  for (i = 0; i < n_children; i++)
+  {
+    if (atk_selection_is_child_selected (ATK_SELECTION (selection_obj), i))
+    {
+      g_print ("%d page selected\n", i);
+    }
+    else
+    {
+      g_print ("%d page not selected\n", i);
+    }
+  }
+  /*
+   * Should not be able to select all items in a notebook.
+   */
+  atk_selection_select_all_selection (ATK_SELECTION (selection_obj));
+  i = atk_selection_get_selection_count (ATK_SELECTION (selection_obj));
+  if ( i != 1)
+  {
+    g_print ("Unexpected selection count: %d, expected 1\n", i);
+    g_print ("\t value of i is: %d\n", i);
+    return;
+  }
+
+  for (i = 0; i < n_children; i++)
+  {
+    atk_selection_add_selection (ATK_SELECTION (selection_obj), i);
+       
+    if (atk_selection_is_child_selected (ATK_SELECTION (selection_obj), i))
+    {
+      g_print ("Page %d: successfully selected\n", i);
+      _finish_selection (selection_obj);
+    }
+    else
+    {
+      g_print ("ERROR: child %d: selection failed\n", i);
+    }
+  }
+  g_print ("*** End _do_selection ***\n");
+  gtk_timeout_add (5000, _remove_page, selection_obj);
+} 
+
+static gint _remove_page (gpointer data)
+{
+  AtkObject *obj = ATK_OBJECT (data);
+  GtkWidget *widget = NULL;
+
+  if (GTK_IS_ACCESSIBLE (obj))
+    widget = GTK_ACCESSIBLE (obj)->widget;
+     
+  g_return_val_if_fail (GTK_IS_NOTEBOOK (widget), FALSE);
+  gtk_notebook_remove_page (GTK_NOTEBOOK (widget), 4);
+  return FALSE;
+}
+
+static gint _finish_selection (gpointer data)
+{
+  AtkObject *obj = ATK_OBJECT (data);
+  AtkObject *selected;
+  AtkObject *parent_object;
+  GtkWidget *parent_widget;
+  gint      i, index;
+
+  g_print ("\t*** Start Finish selection ***\n");
+  
+  i = atk_selection_get_selection_count (ATK_SELECTION (obj));
+  if (i != 1)
+  {
+    g_print ("\tUnexpected selection count: %d, expected 1\n", i);
+    return FALSE;
+  }
+  selected = atk_selection_ref_selection (ATK_SELECTION (obj), 0);
+  g_return_val_if_fail (selected != NULL, FALSE);
+  
+  g_print ("\t*** Selected Item ***\n");
+  index = atk_object_get_index_in_parent (selected);
+  g_print ("\tIndex in parent is: %d\n", index);
+  
+  parent_object = atk_object_get_parent (selected);
+  g_return_val_if_fail (ATK_IS_OBJECT (parent_object), FALSE);
+  g_return_val_if_fail (parent_object == obj, FALSE);
+  parent_widget = GTK_ACCESSIBLE (parent_object)->widget;
+  g_return_val_if_fail (GTK_IS_NOTEBOOK (parent_widget), FALSE);
+  
+  _print_type (selected);
+  i = atk_selection_get_selection_count (ATK_SELECTION (obj));
+  g_return_val_if_fail (i == 1, FALSE);
+  g_object_unref (selected);
+  g_print ("\t*** End Finish selection ***\n");
+  return FALSE;
+}
+  
+  
+static void
+_create_event_watcher (void)
+{
+  atk_add_focus_tracker (_do_selection);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+  g_print("testnotebook Module loaded\n");
+
+  _create_event_watcher();
+
+  return 0;
+}
diff --git a/modules/other/gail/tests/testobject.c b/modules/other/gail/tests/testobject.c
new file mode 100644 (file)
index 0000000..c96516d
--- /dev/null
@@ -0,0 +1,339 @@
+#include <gtk/gtk.h>
+#include "testlib.h"
+
+static void _print_accessible (AtkObject *obj);
+static void _print_type (AtkObject *obj);
+static void _print_states (AtkObject *obj);
+static void _check_children (AtkObject *obj);
+static void _check_relation (AtkObject *obj);
+static void _create_event_watcher (void);
+static void _focus_handler (AtkObject *obj, gboolean focus_in);
+static gboolean _children_changed (GSignalInvocationHint *ihint,
+                                   guint                  n_param_values,
+                                   const GValue          *param_values,
+                                   gpointer               data);
+
+static guint id;
+
+static void _print_states (AtkObject *obj)
+{
+  AtkStateSet *state_set;
+  gint i;
+
+  state_set = atk_object_ref_state_set (obj);
+
+  g_print ("*** Start states ***\n");
+  for (i = 0; i < 64; i++)
+    {
+       AtkStateType one_state;
+       G_CONST_RETURN gchar *name;
+
+       if (atk_state_set_contains_state (state_set, i))
+         {
+           one_state = i;
+
+           name = atk_state_type_get_name (one_state);
+
+           if (name)
+             g_print("%s\n", name);
+         }
+    }
+  g_object_unref (state_set);
+  g_print ("*** End states ***\n");
+}
+
+static void _print_type (AtkObject *obj)
+{
+  G_CONST_RETURN gchar * typename = NULL;
+  G_CONST_RETURN gchar * name = NULL;
+  AtkRole role;
+  static gboolean in_print_type = FALSE;
+   
+  if (GTK_IS_ACCESSIBLE (obj))
+    {
+      GtkWidget* widget = NULL;
+
+      widget = GTK_ACCESSIBLE (obj)->widget;
+      typename = g_type_name (GTK_OBJECT_TYPE (widget));
+      g_print ("Widget type name: %s\n", typename ? typename : "NULL");
+    }
+  typename = g_type_name (G_OBJECT_TYPE (obj));
+  g_print ("Accessible type name: %s\n", typename ? typename : "NULL");
+  name = atk_object_get_name (obj);
+  g_print("Accessible Name: %s\n", (name) ? name : "NULL");
+  role = atk_object_get_role (obj);
+  g_print ("Accessible Role: %s\n", atk_role_get_name (role));
+
+  if (ATK_IS_COMPONENT (obj))
+    {
+      gint x, y, width, height;
+      AtkStateSet *states;
+
+      _print_states (obj);
+      states = atk_object_ref_state_set (obj);
+      if (atk_state_set_contains_state (states, ATK_STATE_VISIBLE))
+        {
+          AtkObject *parent;
+
+          atk_component_get_extents (ATK_COMPONENT (obj), 
+                                     &x, &y, &width, &height, 
+                                     ATK_XY_SCREEN);
+          g_print ("ATK_XY_SCREEN: x: %d y: %d width: %d height: %d\n",
+                   x, y, width, height);
+
+          atk_component_get_extents (ATK_COMPONENT (obj), 
+                                     &x, &y, &width, &height, 
+                                     ATK_XY_WINDOW);
+          g_print ("ATK_XY_WINDOW: x: %d y: %d width: %d height: %d\n", 
+                   x, y, width, height);
+          if (atk_state_set_contains_state (states, ATK_STATE_SHOWING) &&
+              ATK_IS_TEXT (obj))
+            {
+              gint offset;
+
+              atk_text_get_character_extents (ATK_TEXT (obj), 1, 
+                                              &x, &y, &width, &height, 
+                                              ATK_XY_WINDOW);
+              g_print ("Character extents : %d %d %d %d\n", 
+                       x, y, width, height);
+              if (width != 0 && height != 0)
+                {
+                  offset = atk_text_get_offset_at_point (ATK_TEXT (obj), 
+                                                         x, y, 
+                                                         ATK_XY_WINDOW);
+                  if (offset != 1)
+                    {
+                      g_print ("Wrong offset returned (%d) %d\n", 1, offset);
+                    }
+                }
+            }
+          if (in_print_type)
+            return;
+
+          parent = atk_object_get_parent (obj);
+          if (!ATK_IS_COMPONENT (parent))
+            {
+              /* Assume toplevel */
+              g_object_unref (G_OBJECT (states));
+              return;
+            }
+#if 0
+          obj1 = atk_component_ref_accessible_at_point (ATK_COMPONENT (parent),
+                                                        x, y, ATK_XY_WINDOW);
+          if (obj != obj1)
+            {
+              g_print ("Inconsistency between object and ref_accessible_at_point\n");
+              in_print_type = TRUE;
+              _print_type (obj1);
+              in_print_type = FALSE;
+            }
+#endif
+        }
+      g_object_unref (G_OBJECT (states));
+    }
+}
+
+static void _print_accessible (AtkObject *obj)
+{
+  GtkWidget* widget = NULL;
+  AtkObject* parent_atk;
+  AtkObject* ref_obj;
+  AtkRole    role;
+  static gboolean first_time = TRUE;
+
+  if (first_time)
+    {
+      first_time = FALSE;
+      atk_add_global_event_listener (_children_changed, 
+                                     "Atk:AtkObject:children_changed");
+    }
+
+  /*
+   * Check that the object returned by the atk_implementor_ref_accessible()
+   * for a widget is the same as the accessible object
+   */
+  if (GTK_IS_ACCESSIBLE (obj))
+    {
+      widget = GTK_ACCESSIBLE (obj)->widget;
+      ref_obj = atk_implementor_ref_accessible (ATK_IMPLEMENTOR (widget));
+      g_assert (ref_obj == obj);
+      g_object_unref (G_OBJECT (ref_obj));
+    }
+  /*
+   * Add a focus handler so we see focus out events as well
+   */
+  if (ATK_IS_COMPONENT (obj))
+    atk_component_add_focus_handler (ATK_COMPONENT (obj), _focus_handler);
+  g_print ("Object:\n");
+  _print_type (obj);
+  _print_states (obj);
+
+  /*
+   * Get the parent object
+   */
+  parent_atk = atk_object_get_parent (obj);
+  if (parent_atk)
+    {
+      g_print ("Parent Object:\n");
+      _print_type (parent_atk);
+      parent_atk = atk_object_get_parent (parent_atk);
+      if (parent_atk)
+        {
+          g_print ("Grandparent Object:\n");
+          _print_type (parent_atk);
+        }
+    } 
+  else 
+    {
+      g_print ("No parent\n");
+    }
+
+  role = atk_object_get_role (obj);
+
+  if ((role == ATK_ROLE_FRAME) || (role == ATK_ROLE_DIALOG))
+    {
+      _check_children (obj);
+    }
+}
+
+static void _check_relation (AtkObject *obj)
+{
+  AtkRelationSet* relation_set = atk_object_ref_relation_set (obj);
+  gint n_relations, i;
+
+  g_return_if_fail (relation_set);
+
+  n_relations = atk_relation_set_get_n_relations (relation_set);
+  for (i = 0; i < n_relations; i++)
+    {
+      AtkRelation* relation = atk_relation_set_get_relation (relation_set, i);
+
+      g_print ("Index: %d Relation type: %d Number: %d\n", i,
+                atk_relation_get_relation_type (relation),
+                atk_relation_get_target (relation)->len);
+    }
+  g_object_unref (relation_set);
+}
+
+static void _check_children (AtkObject *obj)
+{
+  gint n_children, i;
+  AtkLayer layer;
+  AtkRole role;
+
+  g_print ("Start Check Children\n");
+  n_children = atk_object_get_n_accessible_children (obj);
+  g_print ("Number of children: %d\n", n_children);
+
+  role = atk_object_get_role (obj);
+
+  if (ATK_IS_COMPONENT (obj))
+    {
+      atk_component_add_focus_handler (ATK_COMPONENT (obj), _focus_handler);
+      layer = atk_component_get_layer (ATK_COMPONENT (obj));
+      if (role == ATK_ROLE_MENU)
+             g_assert (layer == ATK_LAYER_POPUP);
+      else
+             g_print ("Layer: %d\n", layer);
+    }
+
+  for (i = 0; i < n_children; i++)
+    {
+      AtkObject *child;
+      AtkObject *parent;
+      int j;
+
+      child = atk_object_ref_accessible_child (obj, i);
+      parent = atk_object_get_parent (child);
+      j = atk_object_get_index_in_parent (child);
+      _print_type (child);
+      _check_relation (child);
+      _check_children (child);
+      if (obj != parent)
+        {
+          g_print ("*** Inconsistency between atk_object_get_parent() and "
+                   "atk_object_ref_accessible_child() ***\n");
+          _print_type (child);
+          _print_type (obj);
+          if (parent)
+            _print_type (parent);
+        }
+      g_object_unref (G_OBJECT (child));
+                 
+      if (j != i)
+        g_print ("*** Inconsistency between parent and children %d %d ***\n",
+                 i, j);
+    }
+  g_print ("End Check Children\n");
+}
+
+static gboolean
+_children_changed (GSignalInvocationHint *ihint,
+                   guint                  n_param_values,
+                   const GValue          *param_values,
+                   gpointer               data)
+{
+  GObject *object;
+  guint index;
+  gpointer target;
+  G_CONST_RETURN gchar *target_name = "NotAtkObject";
+
+  object = g_value_get_object (param_values + 0);
+  index = g_value_get_uint (param_values + 1);
+  target = g_value_get_pointer (param_values + 2);
+  if (G_IS_OBJECT (target))
+    {
+      if (ATK_IS_OBJECT (target))
+        {
+          target_name = atk_object_get_name (target);
+        }
+      if (!target_name) 
+        target_name = g_type_name (G_OBJECT_TYPE (G_OBJECT (target)));
+    }
+  else
+    {
+      if (!target)
+        {
+          AtkObject *child;
+
+          child = atk_object_ref_accessible_child (ATK_OBJECT (object), index);
+          if (child)
+            {
+              target_name = g_type_name (G_OBJECT_TYPE (G_OBJECT (child)));
+              g_object_unref (child);
+            }
+        }
+    }
+  g_print ("_children_watched: %s %s %s index: %d\n", 
+           g_type_name (G_OBJECT_TYPE (object)),
+           g_quark_to_string (ihint->detail),
+           target_name, index);
+  return TRUE;
+}
+
+static void
+_create_event_watcher (void)
+{
+  /*
+   * _print_accessible() will be called for an accessible object when its
+   * widget receives focus.
+   */
+  id = atk_add_focus_tracker (_print_accessible);
+}
+
+static void 
+_focus_handler (AtkObject *obj, gboolean focus_in)
+{
+  g_print ("In _focus_handler focus_in: %s\n", focus_in ? "true" : "false"); 
+  _print_type (obj);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+  g_print("testobject Module loaded\n");
+
+  _create_event_watcher();
+
+  return 0;
+}
diff --git a/modules/other/gail/tests/testoptionmenu.c b/modules/other/gail/tests/testoptionmenu.c
new file mode 100644 (file)
index 0000000..3e9e74c
--- /dev/null
@@ -0,0 +1,154 @@
+#include <gtk/gtk.h>
+#include "testlib.h"
+
+/*
+ * This module is used to test the accessible implementation for GtkOptionMenu
+ *
+ * When the GtkOption menu in the FileSelectionDialog is tabbed to, the menu
+ * is opened and the second item in the menu is selected which causes the 
+ * menu to be closed and the item in the GtkOptionMenu to be updated.
+ */
+#define NUM_VALID_ROLES 1
+
+static void _create_event_watcher (void);
+static void _check_object (AtkObject *obj);
+static gint _do_menu_item_action (gpointer data);
+static gboolean doing_action = FALSE;
+
+static void 
+_check_object (AtkObject *obj)
+{
+  AtkRole role;
+  static G_CONST_RETURN char *name = NULL;
+  static gboolean first_time = TRUE;
+
+  role = atk_object_get_role (obj);
+  if (role == ATK_ROLE_PUSH_BUTTON)
+  /*
+   * Find the specified optionmenu item
+   */
+    {
+      AtkRole valid_roles[NUM_VALID_ROLES];
+      AtkObject *atk_option_menu;
+      GtkWidget *widget;
+
+      if (name == NULL)
+      {
+        name = g_getenv ("TEST_ACCESSIBLE_NAME");
+        if (name == NULL)
+          name = "foo";
+      }
+      valid_roles[0] = ATK_ROLE_PUSH_BUTTON;
+      atk_option_menu = find_object_by_accessible_name_and_role (obj, name,
+                               valid_roles, NUM_VALID_ROLES);
+
+      if (atk_option_menu == NULL)
+        {
+          g_print ("Object not found for %s\n", name);
+          return;
+        }
+      else
+        {
+          g_print ("Object found for %s\n", name);
+        }
+
+
+      g_assert (GTK_IS_ACCESSIBLE (atk_option_menu));
+      widget = GTK_ACCESSIBLE (atk_option_menu)->widget;
+      g_assert (GTK_IS_OPTION_MENU (widget));
+
+      if (first_time)
+        first_time = FALSE;
+      else
+        return;
+
+      /*
+       * This action opens the GtkOptionMenu whose name is "foo" or whatever
+       * was specified in the environment variable TEST_ACCESSIBLE_NAME
+       */
+      atk_action_do_action (ATK_ACTION (atk_option_menu), 0);
+    }
+  else if ((role == ATK_ROLE_MENU_ITEM) ||
+           (role == ATK_ROLE_CHECK_MENU_ITEM) ||
+           (role == ATK_ROLE_RADIO_MENU_ITEM) ||
+           (role == ATK_ROLE_TEAR_OFF_MENU_ITEM))
+    {
+      AtkObject *parent, *child;
+      AtkRole parent_role;
+
+      /*
+       * If we receive focus while waiting for the menu to be closed
+       * we return immediately
+       */
+      if (doing_action)
+        return;
+
+      parent = atk_object_get_parent (obj);
+      parent_role = atk_object_get_role (parent);
+      g_assert (parent_role == ATK_ROLE_MENU);
+    
+      child = atk_object_ref_accessible_child (parent, 1);
+      doing_action = TRUE;
+      gtk_timeout_add (5000, _do_menu_item_action, child);
+    }
+  else
+    {
+      G_CONST_RETURN char *accessible_name;
+
+      accessible_name = atk_object_get_name (obj);
+      if (accessible_name)
+        {
+          g_print ("Name: %s\n", accessible_name);
+        } 
+      else if (GTK_IS_ACCESSIBLE (obj))
+        {
+          GtkWidget *widget = GTK_ACCESSIBLE (obj)->widget;
+          g_print ("Type: %s\n", g_type_name (G_OBJECT_TYPE (widget)));
+        } 
+      if (role == ATK_ROLE_TABLE)
+        {
+          gint n_cols, i;
+
+          n_cols = atk_table_get_n_columns (ATK_TABLE (obj));
+          g_print ("Number of Columns: %d\n", n_cols);
+
+          for (i  = 0; i < n_cols; i++)
+            {
+              AtkObject *header;
+
+              header = atk_table_get_column_header (ATK_TABLE (obj), i);
+              g_print ("header: %s %s\n", 
+                           g_type_name (G_OBJECT_TYPE (header)),
+                           atk_object_get_name (header));
+            }
+        }
+    }
+}
+
+static gint _do_menu_item_action (gpointer data)
+{
+  AtkObject *obj = ATK_OBJECT (data);
+
+  atk_action_do_action (ATK_ACTION (obj), 0);
+  doing_action = FALSE;
+
+  g_object_unref (obj);
+
+  return FALSE;
+}
+
+static void
+_create_event_watcher (void)
+{
+  atk_add_focus_tracker (_check_object);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+  g_print("testoptionmenu Module loaded\n");
+
+  _create_event_watcher();
+
+  return 0;
+}
diff --git a/modules/other/gail/tests/testpaned.c b/modules/other/gail/tests/testpaned.c
new file mode 100644 (file)
index 0000000..ae39364
--- /dev/null
@@ -0,0 +1,133 @@
+#include <string.h>
+#include <stdlib.h>
+#include <gtk/gtk.h>
+#include <testlib.h>
+
+static gint _test_paned (gpointer data);
+static void _check_paned (AtkObject *obj);
+
+static void _property_change_handler (AtkObject   *obj,
+                                      AtkPropertyValues *values);
+
+#define NUM_VALID_ROLES 1
+static gint last_position;
+
+static void _property_change_handler (AtkObject   *obj,
+                                      AtkPropertyValues   *values)
+{
+  G_CONST_RETURN gchar *type_name = g_type_name (G_TYPE_FROM_INSTANCE (obj));
+  G_CONST_RETURN gchar *name = atk_object_get_name (obj);
+
+  g_print ("_property_change_handler: Accessible Type: %s\n",
+           type_name ? type_name : "NULL");
+  g_print ("_property_change_handler: Accessible name: %s\n",
+           name ? name : "NULL");
+  g_print ("_property_change_handler: PropertyName: %s\n",
+           values->property_name ? values->property_name: "NULL");
+  if (strcmp (values->property_name, "accessible-value") == 0)
+  {
+    GValue *value, val;
+    int position;
+    value = &val;
+
+    memset (value, 0, sizeof (GValue));
+    atk_value_get_current_value (ATK_VALUE (obj), value);
+    g_return_if_fail (G_VALUE_HOLDS_INT (value));
+    position = g_value_get_int (value); 
+    g_print ("Position is  %d previous position was %d\n", 
+             position, last_position);
+    last_position = position;
+    atk_value_get_minimum_value (ATK_VALUE (obj), value);
+    g_return_if_fail (G_VALUE_HOLDS_INT (value));
+    position = g_value_get_int (value); 
+    g_print ("Minimum Value is  %d\n", position); 
+    atk_value_get_maximum_value (ATK_VALUE (obj), value);
+    g_return_if_fail (G_VALUE_HOLDS_INT (value));
+    position = g_value_get_int (value); 
+    g_print ("Maximum Value is  %d\n", position); 
+  }
+}
+static gint _test_paned (gpointer data)
+{
+  AtkObject *obj = ATK_OBJECT (data);
+  AtkRole role = atk_object_get_role (obj);
+  GtkWidget *widget;
+  static gint times = 0;
+
+  widget = GTK_ACCESSIBLE (obj)->widget;
+
+  if (role == ATK_ROLE_SPLIT_PANE)
+  {
+    GValue *value, val;
+    int position;
+    value = &val;
+
+    memset (value, 0, sizeof (GValue));
+    atk_value_get_current_value (ATK_VALUE (obj), value);
+    g_return_val_if_fail (G_VALUE_HOLDS_INT (value), FALSE);
+    position = g_value_get_int (value); 
+    g_print ("Position is : %d\n", position);
+    last_position = position;
+    position *= 2;
+    g_value_set_int (value, position);
+    atk_value_set_current_value (ATK_VALUE (obj), value);
+    times++;
+  }
+  if (times < 4)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+static void _check_paned (AtkObject *obj)
+{
+  static gboolean done_paned = FALSE;
+  AtkRole role;
+
+  role = atk_object_get_role (obj);
+
+  if (role == ATK_ROLE_FRAME)
+  {
+    AtkRole roles[NUM_VALID_ROLES];
+    AtkObject *paned_obj;
+
+    if (done_paned)
+      return;
+
+    roles[0] = ATK_ROLE_SPLIT_PANE;
+
+    paned_obj = find_object_by_role (obj, roles, NUM_VALID_ROLES);
+
+    if (paned_obj)
+    {
+      if (!done_paned)
+      {
+        done_paned = TRUE;
+      }
+      atk_object_connect_property_change_handler (paned_obj,
+                   (AtkPropertyChangeHandler*) _property_change_handler);
+      gtk_timeout_add (2000, _test_paned, paned_obj);
+    }
+
+    return;
+  }
+  if (role != ATK_ROLE_COMBO_BOX)
+    return;
+}
+
+static void
+_create_event_watcher (void)
+{
+  atk_add_focus_tracker (_check_paned);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+  g_print("testpaned Module loaded\n");
+
+  _create_event_watcher();
+
+  return 0;
+}
diff --git a/modules/other/gail/tests/testprops.c b/modules/other/gail/tests/testprops.c
new file mode 100644 (file)
index 0000000..8db555e
--- /dev/null
@@ -0,0 +1,220 @@
+#include <string.h>
+#include <stdlib.h>
+#include <atk/atk.h>
+#include <gtk/gtk.h>
+#include <testlib.h>
+
+static void _traverse_children (AtkObject *obj);
+static void _add_handler (AtkObject *obj);
+static void _check_properties (AtkObject *obj);
+static void _property_change_handler (AtkObject   *obj,
+                                      AtkPropertyValues *values);
+static void _state_changed (AtkObject   *obj,
+                            const gchar *name,
+                            gboolean    set);
+static void _selection_changed (AtkObject   *obj);
+static void _visible_data_changed (AtkObject   *obj);
+static void _model_changed (AtkObject   *obj);
+static void _create_event_watcher (void);
+
+static guint id;
+
+static void 
+_state_changed (AtkObject   *obj,
+                const gchar *name,
+                gboolean    set)
+{
+  g_print ("_state_changed: %s: state %s %s\n", 
+           g_type_name (G_OBJECT_TYPE (obj)),
+           set ? "is" : "was", name);
+}
+
+static void 
+_selection_changed (AtkObject   *obj)
+{
+  gchar *type;
+
+  if (ATK_IS_TEXT (obj))
+    type = "text";
+  else if (ATK_IS_SELECTION (obj))
+    type = "child selection";
+  else
+    {
+      g_assert_not_reached();
+      return;
+    }
+
+  g_print ("In selection_changed signal handler for %s, object type: %s\n",
+           type, g_type_name (G_OBJECT_TYPE (obj)));
+}
+
+static void 
+_visible_data_changed (AtkObject   *obj)
+{
+  g_print ("In visible_data_changed signal handler, object type: %s\n",
+           g_type_name (G_OBJECT_TYPE (obj)));
+}
+
+static void 
+_model_changed (AtkObject   *obj)
+{
+  g_print ("In model_changed signal handler, object type: %s\n",
+           g_type_name (G_OBJECT_TYPE (obj)));
+}
+
+static void 
+_property_change_handler (AtkObject   *obj,
+                          AtkPropertyValues   *values)
+{
+  G_CONST_RETURN gchar *type_name = g_type_name (G_TYPE_FROM_INSTANCE (obj));
+  G_CONST_RETURN gchar *name = atk_object_get_name (obj);
+
+  g_print ("_property_change_handler: Accessible Type: %s\n",
+           type_name ? type_name : "NULL");
+  g_print ("_property_change_handler: Accessible name: %s\n",
+           name ? name : "NULL");
+  g_print ("_property_change_handler: PropertyName: %s\n",
+           values->property_name ? values->property_name: "NULL");
+  if (G_VALUE_HOLDS_STRING (&values->new_value))
+    g_print ("_property_change_handler: PropertyValue: %s\n",
+             g_value_get_string (&values->new_value));
+  else if (strcmp (values->property_name, "accessible-child") == 0)
+    {
+      if (G_IS_VALUE (&values->old_value))
+        {
+          g_print ("Child is removed: %s\n", 
+               g_type_name (G_TYPE_FROM_INSTANCE (g_value_get_pointer (&values->old_value))));
+        }
+      if (G_IS_VALUE (&values->new_value))
+        {
+          g_print ("Child is added: %s\n", 
+               g_type_name (G_TYPE_FROM_INSTANCE (g_value_get_pointer (&values->new_value))));
+        }
+    }
+  else if (strcmp (values->property_name, "accessible-parent") == 0)
+    {
+      if (G_IS_VALUE (&values->old_value))
+        {
+          g_print ("Parent is removed: %s\n", 
+               g_type_name (G_TYPE_FROM_INSTANCE (g_value_get_pointer (&values->old_value))));
+        }
+      if (G_IS_VALUE (&values->new_value))
+        {
+          g_print ("Parent is added: %s\n", 
+               g_type_name (G_TYPE_FROM_INSTANCE (g_value_get_pointer (&values->new_value))));
+        }
+    }
+  else if (strcmp (values->property_name, "accessible-value") == 0)
+    {
+      if (G_VALUE_HOLDS_DOUBLE (&values->new_value))
+        {
+          g_print ("Value now is (double) %f\n", 
+                   g_value_get_double (&values->new_value));
+        }
+    }
+}
+
+static void 
+_traverse_children (AtkObject *obj)
+{
+  gint n_children, i;
+  AtkRole role;
+  role = atk_object_get_role (obj);
+
+  if ((role == ATK_ROLE_TABLE) ||
+      (role == ATK_ROLE_TREE_TABLE))
+    return;
+
+  n_children = atk_object_get_n_accessible_children (obj);
+  for (i = 0; i < n_children; i++)
+    {
+      AtkObject *child;
+
+      child = atk_object_ref_accessible_child (obj, i);
+      _add_handler (child);
+      _traverse_children (child);
+      g_object_unref (G_OBJECT (child));
+    }
+}
+
+static void 
+_add_handler (AtkObject *obj)
+{
+  static GPtrArray *obj_array = NULL;
+  gboolean found = FALSE;
+  gint i;
+
+  /*
+   * We create a property handler for each object if one was not associated 
+   * with it already.
+   *
+   * We add it to our array of objects which have property handlers; if an
+   * object is destroyed it remains in the array.
+   */
+  if (obj_array == NULL)
+    obj_array = g_ptr_array_new ();
+  for (i = 0; i < obj_array->len; i++)
+    {
+      if (obj == g_ptr_array_index (obj_array, i))
+        {
+          found = TRUE;
+          break;
+        }
+    }
+  if (!found)
+    {
+      atk_object_connect_property_change_handler (obj,
+                   (AtkPropertyChangeHandler*) _property_change_handler);
+      g_signal_connect (obj, "state-change", 
+                        (GCallback) _state_changed, NULL);
+      if (ATK_IS_SELECTION (obj))
+        g_signal_connect (obj, "selection_changed", 
+                          (GCallback) _selection_changed, NULL);
+      g_signal_connect (obj, "visible_data_changed", 
+                        (GCallback) _visible_data_changed, NULL);
+      if (ATK_IS_TABLE (obj))
+        g_signal_connect (obj, "model_changed", 
+                        (GCallback) _model_changed, NULL);
+      g_ptr_array_add (obj_array, obj);
+    }
+}
+static void 
+_check_properties (AtkObject *obj)
+{
+  AtkRole role;
+
+  g_print ("Start of _check_properties: %s\n", 
+           g_type_name (G_OBJECT_TYPE (obj)));
+
+  _add_handler (obj);
+
+  role = atk_object_get_role (obj);
+  if (role == ATK_ROLE_FRAME ||
+      role == ATK_ROLE_DIALOG)
+    {
+      /*
+       * Add handlers to all children.
+       */
+      _traverse_children (obj);
+    }
+  g_print ("End of _check_properties\n");
+}
+
+static void
+_create_event_watcher (void)
+{
+  id = atk_add_focus_tracker (_check_properties);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+  g_print("testprops Module loaded\n");
+
+  _create_event_watcher();
+
+  return 0;
+}
diff --git a/modules/other/gail/tests/testselection.c b/modules/other/gail/tests/testselection.c
new file mode 100644 (file)
index 0000000..0311663
--- /dev/null
@@ -0,0 +1,198 @@
+#include <string.h>
+#include <atk/atk.h>
+#include <gtk/gtk.h>
+
+/*
+ * This module tests the selection interface on menu items.
+ * To use this module run the test program testgtk and use the menus 
+ * option.
+ */
+static void _do_selection (AtkObject *obj);
+static gint _finish_selection (gpointer data);
+static AtkObject* _find_object (AtkObject* obj, AtkRole role);
+static void _print_type (AtkObject *obj);
+
+static AtkObject* 
+_find_object (AtkObject *obj, 
+              AtkRole   role)
+{
+  /*
+   * Find the first object which is a descendant of the specified object
+   * which matches the specified role.
+   *
+   * This function returns a reference to the AtkObject which should be
+   * removed when finished with the object.
+   */
+  gint i;
+  gint n_children;
+  AtkObject *child;
+
+  n_children = atk_object_get_n_accessible_children (obj);
+  for (i = 0; i < n_children; i++) 
+  {
+    AtkObject* found_obj;
+
+    child = atk_object_ref_accessible_child (obj, i);
+    if (atk_object_get_role (child) == role)
+    {
+      return child;
+    }
+    found_obj = _find_object (child, role);
+    g_object_unref (child);
+    if (found_obj)
+    {
+      return found_obj;
+    }
+  }
+  return NULL;
+}
+
+static void _print_type (AtkObject *obj)
+{
+  G_CONST_RETURN gchar * typename = NULL;
+  G_CONST_RETURN gchar * name = NULL;
+  AtkRole role;
+
+  if (GTK_IS_ACCESSIBLE (obj))
+  {
+    GtkWidget* widget = NULL;
+
+    widget = GTK_ACCESSIBLE (obj)->widget;
+    typename = g_type_name (GTK_OBJECT_TYPE (widget));
+    g_print ("Widget type name: %s\n", typename ? typename : "NULL");
+  }
+  typename = g_type_name (G_OBJECT_TYPE (obj));
+  g_print ("Accessible type name: %s\n", typename ? typename : "NULL");
+  name = atk_object_get_name (obj);
+  g_print("Accessible Name: %s\n", (name) ? name : "NULL");
+  role = atk_object_get_role(obj);
+  g_print ("Accessible Role: %d\n", role);
+}
+
+static void 
+_do_selection (AtkObject *obj)
+{
+  gint i;
+  AtkObject *selected;
+  AtkRole role;
+  AtkObject *selection_obj;
+
+  role = atk_object_get_role (obj);
+
+  if ((role == ATK_ROLE_FRAME) &&
+      (strcmp (atk_object_get_name (obj), "menus") == 0))
+  {
+    selection_obj = _find_object (obj, ATK_ROLE_MENU_BAR);
+    if (selection_obj)
+    {
+      g_object_unref (selection_obj);
+    }
+  }
+  else if (role == ATK_ROLE_COMBO_BOX)
+  {
+    selection_obj = obj;
+  }
+  else
+    return;
+
+  g_print ("*** Start do_selection ***\n");
+
+  
+  if (!selection_obj)
+  {
+    g_print ("no selection_obj\n");
+    return;
+  }
+
+  i = atk_selection_get_selection_count (ATK_SELECTION (selection_obj));
+  if (i != 0)
+  {
+    for (i = 0; i < atk_object_get_n_accessible_children (selection_obj); i++)
+    {
+      if (atk_selection_is_child_selected (ATK_SELECTION (selection_obj), i))
+      {
+        g_print ("%d child selected\n", i);
+      }
+      else
+      {
+        g_print ("%d child not selected\n", i);
+      }
+    } 
+  } 
+  /*
+   * Should not be able to select all items on a menu bar
+   */
+  atk_selection_select_all_selection (ATK_SELECTION (selection_obj));
+  i = atk_selection_get_selection_count (ATK_SELECTION (selection_obj));
+  if ( i != 0)
+  {
+    g_print ("Unexpected selection count: %d, expected 0\n", i);
+    return;
+  }
+  /*
+   * There should not be any items selected
+   */
+  selected = atk_selection_ref_selection (ATK_SELECTION (selection_obj), 0);
+  if ( selected != NULL)
+  {
+    g_print ("Unexpected selection: %d, expected 0\n", i);
+  }
+  atk_selection_add_selection (ATK_SELECTION (selection_obj), 1);
+  gtk_timeout_add (2000, _finish_selection, selection_obj);
+  g_print ("*** End _do_selection ***\n");
+} 
+
+static gint _finish_selection (gpointer data)
+{
+  AtkObject *obj = ATK_OBJECT (data);
+  AtkObject *selected;
+  gint      i;
+  gboolean is_selected;
+
+  g_print ("*** Start Finish selection ***\n");
+
+  /*
+   * If being run for for menus, at this point menu item foo should be 
+   * selected which means that its submenu should be visible.
+   */
+  i = atk_selection_get_selection_count (ATK_SELECTION (obj));
+  if (i != 1)
+  {
+    g_print ("Unexpected selection count: %d, expected 1\n", i);
+    return FALSE;
+  }
+  selected = atk_selection_ref_selection (ATK_SELECTION (obj), 0);
+  g_return_val_if_fail (selected != NULL, FALSE);
+  g_print ("*** Selected Item ***\n");
+  _print_type (selected);
+  g_object_unref (selected);
+  is_selected = atk_selection_is_child_selected (ATK_SELECTION (obj), 1);
+  g_return_val_if_fail (is_selected, FALSE);
+  is_selected = atk_selection_is_child_selected (ATK_SELECTION (obj), 0);
+  g_return_val_if_fail (!is_selected, FALSE);
+  selected = atk_selection_ref_selection (ATK_SELECTION (obj), 1);
+  g_return_val_if_fail (selected == NULL, FALSE);
+  atk_selection_remove_selection (ATK_SELECTION (obj), 0);
+  i = atk_selection_get_selection_count (ATK_SELECTION (obj));
+  g_return_val_if_fail (i == 0, FALSE);
+  selected = atk_selection_ref_selection (ATK_SELECTION (obj), 0);
+  g_return_val_if_fail (selected == NULL, FALSE);
+  g_print ("*** End Finish selection ***\n");
+  return FALSE;
+}
+
+static void
+_create_event_watcher (void)
+{
+  atk_add_focus_tracker (_do_selection);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+  g_print("testselection Module loaded\n");
+
+  _create_event_watcher();
+
+  return 0;
+}
diff --git a/modules/other/gail/tests/teststatusbar.c b/modules/other/gail/tests/teststatusbar.c
new file mode 100644 (file)
index 0000000..97d6abb
--- /dev/null
@@ -0,0 +1,127 @@
+#include <string.h>
+#include <glib-object.h>
+#include <atk/atk.h>
+
+/*
+ * To use this test module, run the test program testgtk and click on 
+ * statusbar
+ */
+
+static void _check_statusbar (AtkObject *obj);
+static AtkObject* _find_object (AtkObject* obj, AtkRole role);
+static void _notify_handler (GObject *obj, GParamSpec *pspec);
+static void _property_change_handler (AtkObject   *obj,
+                                      AtkPropertyValues *values);
+
+static AtkObject*
+_find_object (AtkObject *obj,
+              AtkRole   role)
+{
+  /*
+   * Find the first object which is a descendant of the specified object
+   * which matches the specified role.
+   *
+   * This function returns a reference to the AtkObject which should be
+   * removed when finished with the object.
+   */
+  gint i;
+  gint n_children;
+  AtkObject *child;
+
+  n_children = atk_object_get_n_accessible_children (obj);
+  for (i = 0; i < n_children; i++)
+  {
+    AtkObject* found_obj;
+
+    child = atk_object_ref_accessible_child (obj, i);
+    if (atk_object_get_role (child) == role)
+    {
+      return child;
+    }
+    found_obj = _find_object (child, role);
+    g_object_unref (child);
+    if (found_obj)
+    {
+      return found_obj;
+    }
+  }
+  return NULL;
+}
+
+static void _property_change_handler (AtkObject   *obj,
+                                      AtkPropertyValues   *values)
+{
+  G_CONST_RETURN gchar *type_name = g_type_name (G_TYPE_FROM_INSTANCE (obj));
+  G_CONST_RETURN gchar *name = atk_object_get_name (obj);
+
+  g_print ("_property_change_handler: Accessible Type: %s\n",
+           type_name ? type_name : "NULL");
+  g_print ("_property_change_handler: Accessible name: %s\n",
+           name ? name : "NULL");
+  g_print ("_property_change_handler: PropertyName: %s\n",
+           values->property_name ? values->property_name: "NULL");
+  if (G_VALUE_HOLDS_STRING (&values->new_value))
+    g_print ("_property_change_handler: PropertyValue: %s\n",
+             g_value_get_string (&values->new_value));
+}
+
+static void _check_statusbar (AtkObject *obj)
+{
+  AtkRole role;
+  AtkObject *statusbar, *label;
+
+  role = atk_object_get_role (obj);
+  if (role != ATK_ROLE_FRAME)
+    return;
+
+  statusbar = _find_object (obj, ATK_ROLE_STATUSBAR); 
+  if (!statusbar)
+    return;
+  g_print ("_check_statusbar\n");
+  label = atk_object_ref_accessible_child (statusbar, 0);
+  g_return_if_fail (label == NULL);
+
+  /*
+   * We get notified of changes to the label
+   */
+  g_signal_connect_closure_by_id (statusbar,
+                                  g_signal_lookup ("notify", 
+                                                   G_OBJECT_TYPE (statusbar)),
+                                  0,
+                                  g_cclosure_new (G_CALLBACK (_notify_handler),
+                                                 NULL, NULL),
+                                  FALSE);
+  atk_object_connect_property_change_handler (statusbar,
+                   (AtkPropertyChangeHandler*) _property_change_handler);
+
+}
+
+static void 
+_notify_handler (GObject *obj, GParamSpec *pspec)
+{
+  AtkObject *atk_obj = ATK_OBJECT (obj);
+  G_CONST_RETURN gchar *name;
+
+  g_print ("_notify_handler: property: %s\n", pspec->name);
+  if (strcmp (pspec->name, "accessible-name") == 0)
+  {
+    name = atk_object_get_name (atk_obj);
+    g_print ("_notify_handler: value: |%s|\n", name ? name : "<NULL>");
+  }
+}
+
+static void
+_create_event_watcher (void)
+{
+  atk_add_focus_tracker (_check_statusbar);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+  g_print("teststatusbar Module loaded\n");
+
+  _create_event_watcher();
+
+  return 0;
+}
diff --git a/modules/other/gail/tests/testtable.c b/modules/other/gail/tests/testtable.c
new file mode 100644 (file)
index 0000000..3e3cf83
--- /dev/null
@@ -0,0 +1,939 @@
+#include <string.h>
+#include "testtextlib.h"
+
+#define NUM_ROWS_TO_LOOP 30
+
+typedef struct
+{
+  GtkWidget *tb_others;
+  GtkWidget *tb_ref_selection;
+  GtkWidget *tb_ref_at;
+  GtkWidget *tb_ref_accessible_child;
+  GtkWidget *child_entry;
+  GtkWidget *row_entry;
+  GtkWidget *col_entry;
+  GtkWidget *index_entry;
+}TestChoice;
+
+static void _check_table (AtkObject *in_obj);
+void table_runtest(AtkObject *);
+static void other_runtest(AtkObject *obj);
+static void ref_at_runtest(AtkObject *obj, gint row, gint col);
+static void ref_accessible_child_runtest(AtkObject *obj, gint childno);
+static void ref_selection_runtest (AtkObject *obj, gint index);
+static void _selection_tests(AtkObject *obj);
+static void _display_header_info(gchar *type,
+  AtkObject *header_obj, gint header_num);
+static void _process_child(AtkObject *child_obj);
+
+static void _notify_table_row_inserted (GObject *obj,
+  gint start_offset, gint length);
+static void _notify_table_column_inserted (GObject *obj,
+  gint start_offset, gint length);
+static void _notify_table_row_deleted (GObject *obj,
+  gint start_offset, gint length);
+static void _notify_table_column_deleted (GObject *obj,
+  gint start_offset, gint length);
+static void _notify_table_row_reordered (GObject *obj);
+static void _notify_table_column_reordered (GObject *obj);
+static void _notify_table_child_added (GObject *obj,
+  gint index, AtkObject *child);
+static void _notify_table_child_removed (GObject *obj,
+  gint index, AtkObject *child);
+static void _property_signal_connect (AtkObject        *obj);
+static void _property_change_handler (AtkObject        *obj,
+  AtkPropertyValues *values);
+
+static gboolean tested_set_headers = FALSE;
+static void test_choice_gui (AtkObject **obj);
+static void nogui_runtest (AtkObject *obj);
+static void nogui_ref_at_runtest (AtkObject *obj);
+static void nogui_process_child (AtkObject *obj);
+
+static TestChoice *tc;
+static gint g_visibleGUI = 0;
+static AtkTable *g_table = NULL;
+static AtkObject *current_obj = NULL;
+gboolean g_done = FALSE;
+gboolean g_properties = TRUE;
+
+/* 
+ * destroy
+ *
+ * Destroy Callback 
+ *
+ */
+static void destroy (GtkWidget *widget, gpointer data)
+{
+  gtk_main_quit();
+}
+
+static void choicecb (GtkWidget *widget, gpointer data)
+{
+  AtkObject **ptr_to_obj = (AtkObject **)data;
+  AtkObject *obj = *ptr_to_obj;
+
+  if (GTK_TOGGLE_BUTTON(tc->tb_others)->active)
+  {
+    other_runtest(obj);
+  }
+  else if (GTK_TOGGLE_BUTTON(tc->tb_ref_selection)->active)
+  {
+    const gchar *indexstr;
+    gint index;
+
+    indexstr = gtk_entry_get_text(GTK_ENTRY(tc->index_entry));
+    index = string_to_int((gchar *)indexstr);
+
+    ref_selection_runtest(obj, index); 
+  }
+  else if (GTK_TOGGLE_BUTTON(tc->tb_ref_at)->active)
+  {
+    const gchar *rowstr, *colstr;
+    gint row, col;
+    rowstr = gtk_entry_get_text(GTK_ENTRY(tc->row_entry));
+    colstr = gtk_entry_get_text(GTK_ENTRY(tc->col_entry));
+    row = string_to_int((gchar *)rowstr);
+    col = string_to_int((gchar *)colstr);
+    ref_at_runtest(obj, row, col);
+  }
+  else if (GTK_TOGGLE_BUTTON(tc->tb_ref_accessible_child)->active)
+  {
+    const gchar *childstr;
+    gint childno;
+    childstr = gtk_entry_get_text(GTK_ENTRY(tc->child_entry));
+    childno = string_to_int((gchar *)childstr);
+
+    ref_accessible_child_runtest(obj, childno);
+  }
+}
+
+static void _check_table (AtkObject *in_obj)
+{
+  AtkObject *obj = NULL;
+  G_CONST_RETURN char *no_properties;
+  G_CONST_RETURN char *no_gui;
+
+  no_properties = g_getenv ("TEST_ACCESSIBLE_NO_PROPERTIES");
+  no_gui = g_getenv ("TEST_ACCESSIBLE_NO_GUI");
+
+  if (no_properties != NULL)
+    g_properties = FALSE;
+  if (no_gui != NULL)
+    g_visibleGUI = 1;
+  
+  obj = find_object_by_type(in_obj, "GailTreeView");
+  if (obj != NULL)
+    g_print("Found GailTreeView table in child!\n");
+  else
+  {
+    obj = find_object_by_type(in_obj, "GailCList");
+    if (obj != NULL)
+      g_print("Found GailCList in child!\n");
+    else
+    {
+      g_print("No object found %s\n", g_type_name (G_OBJECT_TYPE (in_obj)));
+      return;
+    }
+  }
+
+  g_print ("In _check_table\n");
+
+  if (!already_accessed_atk_object(obj))
+  {
+    /* Set up signal handlers */
+
+    g_print ("Adding signal handler\n");
+    g_signal_connect_closure_by_id (obj,
+               g_signal_lookup ("column_inserted", G_OBJECT_TYPE (obj)),
+               0,
+               g_cclosure_new (G_CALLBACK (_notify_table_column_inserted),
+               NULL, NULL),
+               FALSE);
+    g_signal_connect_closure_by_id (obj,
+               g_signal_lookup ("row_inserted", G_OBJECT_TYPE (obj)),
+               0,
+                g_cclosure_new (G_CALLBACK (_notify_table_row_inserted),
+                       NULL, NULL),
+               FALSE);
+    g_signal_connect_closure_by_id (obj,
+               g_signal_lookup ("column_deleted", G_OBJECT_TYPE (obj)),
+               0,
+                g_cclosure_new (G_CALLBACK (_notify_table_column_deleted),
+                       NULL, NULL),
+               FALSE);
+    g_signal_connect_closure_by_id (obj,
+               g_signal_lookup ("row_deleted", G_OBJECT_TYPE (obj)),
+               0,
+                g_cclosure_new (G_CALLBACK (_notify_table_row_deleted),
+                       NULL, NULL),
+               FALSE);
+    g_signal_connect_closure_by_id (obj,
+               g_signal_lookup ("column_reordered", G_OBJECT_TYPE (obj)),
+               0,
+                g_cclosure_new (G_CALLBACK (_notify_table_column_reordered),
+                       NULL, NULL),
+               FALSE);
+    g_signal_connect_closure_by_id (obj,
+               g_signal_lookup ("row_reordered", G_OBJECT_TYPE (obj)),
+               0,
+                g_cclosure_new (G_CALLBACK (_notify_table_row_reordered),
+                       NULL, NULL),
+               FALSE);
+    g_signal_connect_closure (obj, "children_changed::add",
+               g_cclosure_new (G_CALLBACK (_notify_table_child_added),
+                        NULL, NULL),
+               FALSE);
+
+    g_signal_connect_closure (obj, "children_changed::remove",
+               g_cclosure_new (G_CALLBACK (_notify_table_child_removed),
+                        NULL, NULL),
+               FALSE);
+
+  }
+  g_table = ATK_TABLE(obj);
+
+  atk_object_connect_property_change_handler (obj,
+                   (AtkPropertyChangeHandler*) _property_change_handler);
+
+  current_obj = obj;
+  /*
+   * The use of &current_obj allows us to change the object being processed
+   * in the GUI.
+   */
+  if (g_visibleGUI != 1)
+    test_choice_gui(&current_obj);
+  else if (no_gui != NULL)
+    nogui_runtest(obj);
+}
+
+static 
+void other_runtest(AtkObject *obj)
+{
+  AtkObject *header_obj;
+  AtkObject *out_obj;
+  G_CONST_RETURN gchar *out_string;
+  GString *out_desc;
+  gint n_cols, n_rows;
+  gint rows_to_loop = NUM_ROWS_TO_LOOP;
+  gint i, j;
+  out_desc = g_string_sized_new(256);
+
+  n_cols = atk_table_get_n_columns(ATK_TABLE(obj));
+  n_rows = atk_table_get_n_rows(ATK_TABLE(obj));
+
+  g_print ("Number of columns is %d\n", n_cols);
+  g_print ("Number of rows is %d\n", n_rows);
+
+  /* Loop NUM_ROWS_TO_LOOP rows if possible */
+  if (n_rows < NUM_ROWS_TO_LOOP)
+     rows_to_loop = n_rows;
+
+  g_print ("\n");
+
+  /* Caption */
+
+  out_obj = atk_table_get_caption(ATK_TABLE(obj));
+  if (out_obj != NULL)
+  {
+    out_string = atk_object_get_name (out_obj);
+    if (out_string)
+      g_print("Caption Name is <%s>\n", out_string);
+    else
+      g_print("Caption has no name\n");
+  }
+  else
+    g_print("No caption\n");
+
+  /* Column descriptions and headers */
+
+  g_print ("\n");
+  for (i=0; i < n_cols; i++)
+  {
+    /* check default */
+    out_string = atk_table_get_column_description(ATK_TABLE(obj), i);
+    if (out_string != NULL)
+      g_print("%d: Column description is <%s>\n", i, out_string);
+    else
+      g_print("%d: Column description is <NULL>\n", i);
+
+    /* check setting a new value */
+    
+    g_string_printf(out_desc, "new column description %d", i);
+
+    if (out_string == NULL || strcmp (out_string, out_desc->str) != 0)
+    {
+      g_print("%d, Setting the column description to <%s>\n",
+        i, out_desc->str);
+      atk_table_set_column_description(ATK_TABLE(obj), i, out_desc->str);
+      out_string = atk_table_get_column_description(ATK_TABLE(obj), i);
+      if (out_string != NULL)
+        g_print("%d: Column description is <%s>\n", i, out_string);
+      else
+        g_print("%d: Column description is <NULL>\n", i);
+    }
+
+    /* Column header */
+    header_obj = atk_table_get_column_header(ATK_TABLE(obj), i);
+    _display_header_info("Column", header_obj, i);
+  }
+
+  /* Row description */
+
+  g_print ("\n");
+
+  for (i=0; i < rows_to_loop; i++)
+  {
+    out_string = atk_table_get_row_description(ATK_TABLE(obj), i);
+    if (out_string != NULL)
+      g_print("%d: Row description is <%s>\n", i, out_string);
+    else
+      g_print("%d: Row description is <NULL>\n", i);
+
+    g_string_printf(out_desc, "new row description %d", i);
+
+    if (out_string == NULL || strcmp (out_string, out_desc->str) != 0)
+    {
+      g_print("%d: Setting the row description to <%s>\n",
+        i, out_desc->str);
+      atk_table_set_row_description(ATK_TABLE(obj), i, out_desc->str);
+
+      out_string = atk_table_get_row_description(ATK_TABLE(obj), i);
+      if (out_string != NULL)
+        g_print("%d: Row description is <%s>\n", i, out_string);
+      else
+        g_print("%d: Row description is <NULL>\n", i);
+    }
+
+    header_obj = atk_table_get_row_header(ATK_TABLE(obj), i);
+    _display_header_info("Row", header_obj, i);
+    
+    for (j=0; j <n_cols; j++)
+    {
+      gint index = atk_table_get_index_at(ATK_TABLE(obj), i, j);
+      gint row, column;
+
+      column = atk_table_get_column_at_index (ATK_TABLE (obj), index);
+      g_return_if_fail (column == j);
+
+      row = atk_table_get_row_at_index (ATK_TABLE (obj), index);
+      g_return_if_fail (row == i);
+
+      if(atk_selection_is_child_selected(ATK_SELECTION(obj), index))
+        g_print("atk_selection_is_child_selected,index = %d returns TRUE\n", index);
+      /* Generic cell tests */
+      /* Just test setting column headers once. */
+
+      if (!tested_set_headers)
+      {
+        tested_set_headers = TRUE;
+   
+        /* Hardcode to 1,1 for now */
+        g_print(
+          "Testing set_column_header for column %d, to table\n",
+          (n_cols - 1));
+        atk_table_set_column_header(ATK_TABLE(obj), (n_cols - 1), obj);
+
+        g_print("Testing set_row_header for row %d, to table\n", n_rows);
+        atk_table_set_row_header(ATK_TABLE(obj), n_rows, obj);
+      }
+    }
+  }
+
+  /* row/column extents */
+
+  g_print("\n");
+  g_print("Row extents at 1,1 is %d\n",
+    atk_table_get_row_extent_at(ATK_TABLE(obj), 1, 1));
+  g_print("Column extents at 1,1 is %d\n",
+    atk_table_get_column_extent_at(ATK_TABLE(obj), 1, 1));
+}
+
+static
+void ref_accessible_child_runtest(AtkObject *obj, gint child)
+{
+  AtkObject *child_obj;
+  /* ref_child */
+  g_print ("Accessing child %d\n", child);
+  child_obj = atk_object_ref_accessible_child (obj, child);
+  _property_signal_connect(child_obj);
+  if (child_obj != NULL)
+     _process_child(child_obj);
+}
+
+static 
+void ref_selection_runtest (AtkObject *obj, gint index)
+{
+  AtkObject *child_obj;
+
+  /* use atk_selection_ref_selection just once to check it works */
+  child_obj = atk_selection_ref_selection(ATK_SELECTION(obj), index);
+  if (child_obj)
+  {
+    g_print("child_obj gotten from atk_selection_ref_selection\n");
+    g_object_unref (child_obj);
+  }
+  else 
+    g_print("NULL returned by atk_selection_ref_selection\n");
+
+  _selection_tests(obj);
+}
+
+static
+void ref_at_runtest(AtkObject *obj, gint row, gint col)
+{
+  AtkObject *child_obj;
+  /* ref_at */
+
+  g_print("Testing ref_at row %d column %d\n", row, col);
+
+  child_obj = atk_table_ref_at(ATK_TABLE(obj), row, col);
+  _property_signal_connect(child_obj);
+
+  g_print("Row is %d, col is %d\n", row, col);
+
+  _process_child(child_obj);
+  if (child_obj)
+    g_object_unref (child_obj);
+}
+
+/**
+ * process_child
+ **/
+static void
+_process_child(AtkObject *child_obj)
+{
+  if (child_obj != NULL)
+  {
+    if (ATK_IS_TEXT(child_obj))
+    {
+      add_handlers(child_obj);
+      setup_gui(child_obj, runtest);
+    }
+    else
+    {
+      g_print("Interface is not text!\n");
+    }
+/*
+    if (ATK_IS_ACTION(child_obj))
+      {
+       gint i, j;
+       gchar *action_name;
+       gchar *action_description;
+       gchar *action_keybinding;
+       AtkAction *action = ATK_ACTION(child_obj);
+
+       i = atk_action_get_n_actions (action);
+       g_print ("Supports AtkAction with %d actions.\n", i);
+       for (j = 0; j < i; j++)
+         {
+           g_print ("Action %d:\n", j);
+           action_name = atk_action_get_name (action, j);
+           if (action_name)
+             g_print (" Name = %s\n", action_name);
+           action_description = atk_action_get_description (action, j);
+           if (action_description)
+             g_print (" Description = %s\n", action_description);
+           action_keybinding = atk_action_get_keybinding (action, j);
+           if (action_keybinding)
+             g_print (" Keybinding = %s\n", action_keybinding);
+           action_description = "new description";
+           g_print (" Setting description to %s\n", action_description);
+           atk_action_set_description (action, j, action_description);
+           action_description = atk_action_get_description (action, j);
+           if (action_description)
+             g_print (" New description is now %s\n", action_description);
+         }
+      }
+*/
+  }
+  else
+  {
+    g_print("Child is NULL!\n");
+  }
+}
+       
+/**
+ * Combined tests on AtkTable and AtkSelection on individual rows rather than 
+ * all of them 
+ **/
+static void
+_selection_tests(AtkObject *obj)
+{
+  gint n_rows = 0;
+  gint n_cols = 0;
+  gint selection_count = 0;
+  gint i = 0;
+  gint *selected = NULL;
+  AtkTable *table;
+
+  table = ATK_TABLE (obj);
+
+  n_rows = atk_table_get_selected_rows(table, &selected);
+  for (i = 0; i < n_rows; i++)
+  {
+    g_print("atk_table_get_selected_row returns : %d\n", 
+              selected[i]);
+    if (!atk_table_is_row_selected (table, selected[i]))
+      g_print("atk_table_is_row_selected returns false for selected row %d\n", 
+              selected[i]);
+  }
+  g_free (selected);
+
+  selected = NULL;
+  n_cols = atk_table_get_selected_columns(table, &selected);
+  for (i = 0; i < n_cols; i++)
+    g_print("atk_table_get_selected_columns returns : %d\n", selected[i]);
+  g_free (selected);
+       
+  selection_count = atk_selection_get_selection_count(ATK_SELECTION(obj));
+  g_print("atk_selection_get_selection_count returns %d\n", selection_count);
+
+  if (atk_table_is_row_selected(table, 2))
+  {
+    g_print("atk_table_is_row_selected (table, 2) returns TRUE\n");
+    atk_selection_clear_selection (ATK_SELECTION (obj));
+    if (atk_table_add_row_selection(table, 4))
+      g_print("atk_table_add_row_selection: selected row 4\n");
+    if (!atk_table_is_row_selected (table, 4))
+      g_print("atk_table_is_row_selected returns false for row 2\n");
+    if (atk_table_is_row_selected (table, 2))
+      g_print("atk_table_is_row_selected gives false positive for row 2\n");
+  }
+
+  if (atk_table_is_row_selected(table, 3))
+  {
+    if (atk_table_remove_row_selection(table, 3))
+      g_print("atk_table_remove_row_selection unselected row 3\n");
+  }
+
+  if (atk_table_is_selected(table, 5, 4))
+  {
+    atk_selection_clear_selection(ATK_SELECTION(obj));
+    g_print("atk_selection_clear_selection: just cleared all selected\n");
+  }
+
+  if (atk_table_is_column_selected(table, 2))
+  {
+    g_print("atk_table_is_column_selected(obj, 2) returns TRUE\n");
+    if (atk_table_add_column_selection(table, 4))
+      g_print("atk_table_add_column_selection: selected column 4\n");
+    g_print("atk_table_is_column_selected(obj, 2) returns TRUE\n");
+  }
+
+  if (atk_table_is_column_selected(table, 3))
+  {
+    if (atk_table_remove_column_selection(table, 3))
+      g_print("atk_table_remove_column_selection: unselected column 3\n");
+  }
+}
+
+static void
+_create_event_watcher (void)
+{
+  atk_add_focus_tracker (_check_table);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+    g_print("TestTable Module loaded\n");
+
+    _create_event_watcher();
+
+    return 0;
+}
+
+static void
+_notify_table_row_inserted (GObject *obj, gint start_offset, gint length)
+{
+  g_print ("SIGNAL - Row inserted at position %d, num of rows inserted %d!\n",
+    start_offset, length);
+}
+
+static void
+_notify_table_column_inserted (GObject *obj, gint start_offset, gint length)
+{
+  g_print ("SIGNAL - Column inserted at position %d, num of columns inserted %d!\n",
+    start_offset, length);
+}
+
+static void
+_notify_table_row_deleted (GObject *obj, gint start_offset, gint length)
+{
+  g_print ("SIGNAL - Row deleted at position %d, num of rows deleted %d!\n",
+    start_offset, length);
+}
+
+static void
+_notify_table_column_deleted (GObject *obj, gint start_offset, gint length)
+{
+  g_print ("SIGNAL - Column deleted at position %d, num of columns deleted %d!\n",
+    start_offset, length);
+}
+
+static void
+_notify_table_row_reordered (GObject *obj)
+{
+  g_print ("SIGNAL - Row reordered!\n");
+}
+
+static void
+_notify_table_column_reordered (GObject *obj)
+{
+  g_print ("SIGNAL - Column reordered!\n");
+}
+
+static void _notify_table_child_added (GObject *obj,
+  gint index, AtkObject *child)
+{
+   g_print ("SIGNAL - Child added - index %d\n", index);
+}
+
+static void _notify_table_child_removed (GObject *obj,
+  gint index, AtkObject *child)
+{
+   g_print ("SIGNAL - Child removed - index %d\n", index);
+}
+
+static void
+_display_header_info(gchar *type, AtkObject *header_obj, gint header_num)
+{
+  if (header_obj != NULL)
+  {
+    AtkRole role;
+    role = atk_object_get_role(header_obj);
+
+    if (role == ATK_ROLE_PUSH_BUTTON)
+    {
+      g_print ("%d: %s header is a push button!\n", header_num, type);
+    }
+    else if (role == ATK_ROLE_LABEL)
+    {
+      g_print ("%d: %s header is a label!\n", header_num, type);
+    }
+    else if (ATK_IS_TEXT(header_obj))
+    {
+      gchar *header_text;
+
+      header_text = atk_text_get_text (ATK_TEXT (header_obj), 0, 3);
+      if (header_text != NULL)
+      {
+        g_print("%d: %s header is a text value <%s>\n", header_num,
+          type, header_text);
+      }
+      else
+      {
+        g_print("%d: %s header is a text value <NULL>\n", header_num,
+          type);
+      }
+    }
+    else 
+    {
+      g_print ("%d: %s header is of type %s!\n", header_num,
+        type, atk_role_get_name (role));
+    }
+  }
+  else
+  {
+    g_print ("%d: %s header object is NULL!\n", header_num, type);
+  }
+}
+
+static void _property_signal_connect (AtkObject *obj)
+{
+  if (g_properties && obj != NULL)
+  {
+    g_signal_connect_closure_by_id (obj,
+    g_signal_lookup ("property_change", G_OBJECT_TYPE (obj)),
+      0,
+      g_cclosure_new (G_CALLBACK (_property_change_handler),
+      NULL, NULL),
+      FALSE);
+  }
+}
+
+static void 
+_property_change_handler (AtkObject         *obj,
+                          AtkPropertyValues *values)
+{
+  gchar *obj_text;
+  const gchar *name;
+
+  if (g_table != NULL)
+  {
+    gint index = atk_object_get_index_in_parent(obj);
+    
+    if (index >= 0)
+      g_print("Index is %d, row is %d, col is %d\n", index,
+              atk_table_get_row_at_index(g_table, index),
+              atk_table_get_column_at_index(g_table, index));
+    else
+      g_print ("index: %d for %s\n", index, g_type_name (G_OBJECT_TYPE (obj)));
+  }
+
+  if (ATK_IS_TEXT(obj))
+  {
+     obj_text = atk_text_get_text (ATK_TEXT (obj), 0, 15);
+     if (obj_text == NULL)
+       g_print("  Cell text is <NULL>\n");
+     else
+       g_print("  Cell text is <%s>\n", obj_text);
+  }
+
+  g_print("  PropertyName <%s>\n",
+          values->property_name ? values->property_name: "NULL");
+  g_print("    - ");
+
+  if (&values->old_value != NULL && G_IS_VALUE (&values->old_value))
+  {
+    GType old_type = G_VALUE_TYPE (&values->old_value);
+
+    switch (old_type)
+    {
+    case G_TYPE_INT:
+      g_print("value was <%d>\n", g_value_get_int (&values->old_value));
+      break;
+    case G_TYPE_STRING:
+      name = g_value_get_string (&values->old_value);
+      if (name != NULL)
+        g_print ("value was <%s>\n", name);
+      else
+        g_print ("value was <NULL>\n");
+      break;
+    default: 
+      g_print("value was <unknown type>\n");
+      break;
+    }
+  }
+  else
+  {
+    g_print("value was <not a value>\n");
+  }
+  g_print("    - ");
+  if (&values->new_value != NULL && G_IS_VALUE (&values->new_value))
+  {
+    GType new_type = G_VALUE_TYPE (&values->new_value);
+
+    switch (new_type)
+    {
+    case G_TYPE_INT:
+      g_print("value is <%d>\n", g_value_get_int (&values->new_value));
+      break;
+    case G_TYPE_STRING:
+      name = g_value_get_string (&values->new_value);
+      if (name != NULL)
+        g_print ("value is <%s>\n", name);
+      else
+        g_print ("value is <NULL>\n");
+      break;
+    default: 
+      g_print("value is <unknown type>\n");
+      break;
+    }
+  }
+  else
+  {
+    g_print("value is <not a value>\n");
+  }
+}
+
+static
+void test_choice_gui(AtkObject **obj)
+{
+  GtkWidget *window;
+  GtkWidget *vbox;
+  GtkWidget *hbox;
+  GtkWidget *child_label;
+  GtkWidget *row_label;
+  GtkWidget *col_label;
+  GtkWidget *index_label;
+  GtkWidget *hseparator;
+  GtkWidget *hbuttonbox;
+  GtkWidget *button;
+
+
+  tc = (TestChoice *) g_malloc (sizeof(TestChoice));
+  
+  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_title(GTK_WINDOW(window), "Test to run");
+
+  g_signal_connect(GTK_OBJECT(window), "destroy",
+  GTK_SIGNAL_FUNC(destroy), &window);
+  vbox = gtk_vbox_new(TRUE, 0);
+  gtk_box_set_spacing(GTK_BOX(vbox), 10);
+
+
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_set_spacing(GTK_BOX(hbox), 10);
+  tc->tb_ref_selection = gtk_toggle_button_new_with_label("ref_selection");
+  gtk_box_pack_start_defaults(GTK_BOX(hbox), tc->tb_ref_selection);
+  index_label = gtk_label_new("index: ");
+  gtk_box_pack_start_defaults(GTK_BOX(hbox), index_label);
+  tc->index_entry = gtk_entry_new();
+  gtk_entry_set_text(GTK_ENTRY(tc->index_entry), "1");
+  gtk_box_pack_start_defaults(GTK_BOX(hbox), tc->index_entry);
+  gtk_box_pack_start_defaults(GTK_BOX(vbox), hbox); 
+
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_set_spacing(GTK_BOX(hbox), 10);
+  tc->tb_ref_at = gtk_toggle_button_new_with_label("ref_at");
+  gtk_box_pack_start_defaults(GTK_BOX(hbox), tc->tb_ref_at);
+  row_label = gtk_label_new("row:");
+  gtk_box_pack_start_defaults(GTK_BOX(hbox), row_label);
+  tc->row_entry = gtk_entry_new();
+  gtk_entry_set_text(GTK_ENTRY(tc->row_entry), "1");
+  gtk_box_pack_start_defaults(GTK_BOX(hbox), tc->row_entry);
+  col_label = gtk_label_new("column:");
+  gtk_box_pack_start_defaults(GTK_BOX(hbox), col_label);
+  tc->col_entry = gtk_entry_new();
+  gtk_entry_set_text(GTK_ENTRY(tc->col_entry), "1");
+  gtk_box_pack_start_defaults(GTK_BOX(hbox), tc->col_entry);
+  gtk_box_pack_start_defaults(GTK_BOX(vbox), hbox); 
+
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_set_spacing(GTK_BOX(hbox), 10);
+  tc->tb_ref_accessible_child = gtk_toggle_button_new_with_label("ref_accessible_child");
+  gtk_box_pack_start_defaults(GTK_BOX(hbox), tc->tb_ref_accessible_child);
+  child_label = gtk_label_new("Child no:");
+  gtk_box_pack_start_defaults(GTK_BOX(hbox), child_label); 
+  tc->child_entry = gtk_entry_new();
+  gtk_entry_set_text(GTK_ENTRY(tc->child_entry), "1");
+  gtk_box_pack_start_defaults(GTK_BOX(hbox), tc->child_entry);
+  gtk_box_pack_start_defaults(GTK_BOX(vbox), hbox);
+
+  tc->tb_others = gtk_toggle_button_new_with_label("others");
+  gtk_box_pack_start_defaults(GTK_BOX(vbox), tc->tb_others);
+  
+  hseparator = gtk_hseparator_new();
+  gtk_box_pack_start_defaults(GTK_BOX(vbox), hseparator);
+
+  button = gtk_button_new_with_mnemonic("_Run Test");
+
+  hbuttonbox = gtk_hbutton_box_new();
+  gtk_button_box_set_layout(GTK_BUTTON_BOX(hbuttonbox),
+    GTK_BUTTONBOX_SPREAD);
+  gtk_box_pack_end_defaults(GTK_BOX(hbuttonbox), GTK_WIDGET(button));
+  gtk_box_pack_end_defaults(GTK_BOX(vbox), hbuttonbox);
+  g_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(choicecb), obj);
+
+  gtk_container_add(GTK_CONTAINER(window), vbox);
+  gtk_widget_show(vbox);
+  gtk_widget_show(window);
+  gtk_widget_show_all(GTK_WIDGET(window));
+
+  g_visibleGUI = 1;
+}
+
+void static
+nogui_runtest (AtkObject *obj)
+{
+  g_print ("Running non-GUI tests...\n");
+  other_runtest (obj);
+  nogui_ref_at_runtest (obj);
+}
+
+static void
+nogui_ref_at_runtest (AtkObject *obj)
+{
+  AtkObject *child_obj;
+  gint i, j;
+  gint n_cols;
+  gint rows_to_loop = 5;
+
+  n_cols = atk_table_get_n_columns (ATK_TABLE(obj));
+  
+  if (atk_table_get_n_rows(ATK_TABLE(obj)) < rows_to_loop)
+    rows_to_loop = atk_table_get_n_rows (ATK_TABLE(obj));
+
+  for (i=0; i < rows_to_loop; i++)
+  {
+    /* Just the first rows_to_loop rows */
+    for (j=0; j < n_cols; j++)
+    {
+      gint index = atk_table_get_index_at(ATK_TABLE(obj), i, j);
+         if(atk_selection_is_child_selected(ATK_SELECTION(obj), index))
+                g_print("atk_selection_is_child_selected,index = %d returns TRUE\n", index);
+
+      g_print("Testing ref_at row %d column %d\n", i, j);
+
+      if (i == 3 && j == 0)
+      {
+        g_print("child_obj gotten from atk_selection_ref_selection\n");
+
+        /* use atk_selection_ref_selection just once to check it works */
+        child_obj = atk_selection_ref_selection(ATK_SELECTION(obj), index );
+      }
+      else     
+      {
+        child_obj = atk_table_ref_at(ATK_TABLE(obj), i, j);
+      }
+
+      _property_signal_connect(child_obj);
+
+      g_print("Index is %d, row is %d, col is %d\n", index,
+        atk_table_get_row_at_index(ATK_TABLE(obj), index),
+        atk_table_get_column_at_index(ATK_TABLE(obj), index));
+
+      nogui_process_child (child_obj);
+
+      /* Generic cell tests */
+      /* Just test setting column headers once. */
+
+      if (!tested_set_headers)
+      {
+        tested_set_headers = TRUE;
+
+        g_print("Testing set_column_header for column %d, to cell value %d,%d\n",
+          j, i, j);
+        atk_table_set_column_header(ATK_TABLE(obj), j, child_obj);
+
+        g_print("Testing set_row_header for row %d, to cell value %d,%d\n",
+          i, i, j);
+        atk_table_set_row_header(ATK_TABLE(obj), i, child_obj);
+      }
+      if (child_obj)
+        g_object_unref (child_obj);
+    }
+  }
+}
+
+static void
+nogui_process_child (AtkObject *obj)
+{
+  gchar default_val[5] = "NULL";
+
+  if (ATK_IS_TEXT(obj))
+    {
+      gchar *current_text;
+      current_text = atk_text_get_text (ATK_TEXT(obj), 0, -1);
+      g_print ("Child supports text interface.\nCurrent text is %s\n", current_text);
+    }
+
+  if (ATK_IS_ACTION(obj))
+    {
+      AtkAction *action = ATK_ACTION(obj);
+      gint n_actions, i;
+      G_CONST_RETURN gchar *name, *description;
+      
+      n_actions = atk_action_get_n_actions (action);
+      g_print ("Child supports %d actions.\n", n_actions);
+      for (i = 0; i < n_actions; i++)
+       {
+         name = atk_action_get_name (action, i);
+         description = atk_action_get_description (action, i);
+
+          if (name == NULL)
+             name = default_val;
+          if (description == NULL)
+             description = default_val;
+          
+         g_print (" %d: name = <%s>\n", i, name);
+          g_print ("    description = <%s>\n", description);
+       }
+    }
+}
+
diff --git a/modules/other/gail/tests/testtext.c b/modules/other/gail/tests/testtext.c
new file mode 100644 (file)
index 0000000..6af191c
--- /dev/null
@@ -0,0 +1,134 @@
+#include <atk/atkeditabletext.h>
+#include "testtextlib.h"
+
+#define NUM_VALID_ROLES 6  
+
+static void _create_event_watcher (void);
+static void _check_text (AtkObject *obj);
+void runtest(AtkObject *, gint);
+
+static guint id1 = 0;
+static guint win_count = 0;
+
+static void _check_text (AtkObject *in_obj)
+{
+  AtkObject *obj = NULL;
+  AtkRole role;
+  gchar* title;
+  AtkRole valid_roles[NUM_VALID_ROLES];
+
+  if (g_getenv("TEST_ACCESSIBLE_DELAY") != NULL)
+  {
+    int max_cnt = string_to_int(g_getenv("TEST_ACCESSIBLE_DELAY"));
+    win_count++;
+    if (win_count <= max_cnt)
+      return;
+  }
+
+  /* Set Up */
+
+  valid_roles[0] = ATK_ROLE_TEXT;
+  valid_roles[1] = ATK_ROLE_LABEL;
+  valid_roles[2] = ATK_ROLE_ACCEL_LABEL;
+  valid_roles[3] = ATK_ROLE_PASSWORD_TEXT;
+  valid_roles[4] = ATK_ROLE_TABLE_CELL;
+  valid_roles[5] = ATK_ROLE_PANEL;
+  
+  /* The following if/else grabs the windows name, or sets title to NULL if none. */
+  if (in_obj->name)
+  {
+     title = in_obj->name;
+  }
+  else
+  {
+    GtkWidget *toplevel;
+    GtkWidget* widget = GTK_ACCESSIBLE (in_obj)->widget;
+
+    if (widget == NULL)
+    {
+      title = NULL;
+    }
+
+    toplevel = gtk_widget_get_toplevel (widget);
+    if (GTK_IS_WINDOW (toplevel) && GTK_WINDOW (toplevel)->title)
+    {
+      title = GTK_WINDOW (toplevel)->title;
+    }
+    else
+      title = NULL;
+  }
+  /* If no window name, do nothing */
+  if (title == NULL) 
+    return;
+  /* 
+   * If testtext test program, find obj just by role since only one child 
+   * with no name
+   */
+  else if (g_ascii_strncasecmp(title, "testtext", 7) == 0) 
+  {
+    obj = find_object_by_role(in_obj, valid_roles, NUM_VALID_ROLES);
+  }
+  /*
+   * Otherwise, get obj by name and role so you can specify exactly which 
+   * obj to run tests on 
+   */
+  else 
+  {
+    G_CONST_RETURN gchar *test_accessible_name = g_getenv ("TEST_ACCESSIBLE_NAME");
+
+    if (test_accessible_name != NULL)
+    {
+      obj = find_object_by_accessible_name_and_role(in_obj,
+        test_accessible_name, valid_roles, NUM_VALID_ROLES);
+    }
+    if (obj != NULL)
+    {
+      if (atk_object_get_role (obj) == ATK_ROLE_PANEL)
+      {
+        /* Get the child and check whether it is a label */
+
+        obj = atk_object_ref_accessible_child (obj, 0);
+        g_assert (atk_object_get_role (obj) == ATK_ROLE_LABEL);
+        g_object_unref (obj);
+      }
+       g_print("Found valid name and role in child!\n");
+    }
+    else
+    {
+       obj = find_object_by_role(in_obj, valid_roles, NUM_VALID_ROLES - 1);
+       if (obj != NULL)
+          g_print("Found valid role in child\n");
+    }   
+  } 
+  if (obj == NULL)
+  {
+     g_print("Object not found\n");
+     return;
+  }
+  role = atk_object_get_role(obj);
+
+  g_print("_check_text - Found role type %s!\n\n", atk_role_get_name (role));
+
+  add_handlers(obj);
+
+  if (!(isVisibleDialog()))
+    setup_gui(obj, runtest);
+  atk_remove_focus_tracker (id1);
+}
+
+static void
+_create_event_watcher (void)
+{
+  id1 = atk_add_focus_tracker (_check_text);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+  g_print("testtext Module loaded.\n");
+  _create_event_watcher();
+
+  return 0;
+}
+
+
diff --git a/modules/other/gail/tests/testtextlib.c b/modules/other/gail/tests/testtextlib.c
new file mode 100644 (file)
index 0000000..38b8d0f
--- /dev/null
@@ -0,0 +1,740 @@
+#include <string.h>
+#include <stdio.h>
+#include "testtextlib.h"
+
+static AtkAttributeSet *attrib = NULL;
+static char result_string[2][6] = {"FALSE", "TRUE"};
+
+/**
+ * setup_gui:
+ * @obj: An @AtkObject
+ * @test: The callback to be run when the "Run Tests" button
+ *   in the GUI is clicked.
+ *
+ * Sets up the GUI windows.
+ *
+ * Returns: the window number, or -1 if failure.
+ **/
+gint setup_gui(AtkObject *obj, TLruntest test)
+{
+  gchar *paramnames[MAX_PARAMS];
+  gchar *defaults[MAX_PARAMS];
+  static OutputWindow *tow = NULL;
+  gint  window;
+  
+  if (tow)
+    window = create_windows(obj, test, &tow);
+  else
+    window = create_windows(obj, test, &tow);
+
+  if (window == -1)
+    return -1;
+
+  /* Get Text [at|after|before] Offset Tests */
+  paramnames[0] = "offset";
+  defaults[0] = "1"; 
+  add_test(window, "atk_text_get_text_after_offset", 1,  paramnames, defaults);
+  add_test(window, "atk_text_get_text_before_offset", 1, paramnames, defaults);
+  add_test(window, "atk_text_get_text_at_offset",1 , paramnames, defaults);
+  
+  /* Get Character Count Test */
+  add_test(window, "atk_text_get_character_count", 0, NULL, NULL); 
+
+  /* Get Character At Offset Test */
+  paramnames[0] = "offset";
+  defaults[0] = "1";
+  add_test(window, "atk_text_get_character_at_offset", 1, paramnames, defaults);
+   
+  /* Get Text Test */
+  paramnames[0] = "position 1";
+  paramnames[1] = "position 2";
+  defaults[0] = "0";
+  defaults[1] = "5";
+  add_test(window, "atk_text_get_text", 2, paramnames, defaults); 
+
+  /* Caret Tests */
+  add_test(window, "atk_text_get_caret_offset", 0, NULL, NULL); 
+
+  paramnames[0] = "offset";
+  defaults[0] = "1";
+  add_test(window, "atk_text_set_caret_offset", 1, paramnames, defaults);
+
+  /* Selection Tests */
+  add_test(window, "atk_text_get_n_selections", 0, NULL, NULL);
+    
+  paramnames[0] = "selection no";
+  defaults[0] = "0";
+  add_test(window, "atk_text_get_selection", 1, paramnames, defaults);  
+
+  paramnames[0] = "start";
+  paramnames[1] = "end";
+  defaults[0] = "3";
+  defaults[1] = "8";
+  add_test(window, "atk_text_add_selection", 2, paramnames, defaults); 
+
+  paramnames[0] = "selection no";
+  paramnames[1] = "start";
+  paramnames[2] = "end";
+  defaults[0] = "0";
+  defaults[1] = "5";
+  defaults[2] = "7"; 
+  add_test(window, "atk_text_set_selection", 3, paramnames, defaults);
+
+  paramnames[0] = "selection no";
+  defaults[0] = "0";
+  add_test(window, "atk_text_remove_selection", 1, paramnames, defaults);
+
+  paramnames[0] = "offset";
+  defaults[0] = "36";
+  add_test(window, "atk_text_get_run_attributes", 1, paramnames, defaults);
+
+  add_test(window, "atk_text_get_default_attributes", 0, paramnames, defaults);
+
+  paramnames[0] = "offset";
+  paramnames[1] = "coord mode";
+  defaults[0] = "0";
+  defaults[1] = "ATK_XY_SCREEN";
+  add_test(window, "atk_text_get_character_extents", 2, paramnames, defaults);
+
+  paramnames[0] = "x";
+  paramnames[1] = "y";
+  paramnames[2] = "coord mode";
+  defaults[0] = "106";
+  defaults[1] = "208";
+  defaults[2] = "ATK_XY_SCREEN";
+  add_test(window, "atk_text_get_offset_at_point", 3, paramnames, defaults);
+
+  /* Editable Text Tests */
+  if (ATK_IS_EDITABLE_TEXT(obj)) 
+  {
+
+    paramnames[0] = "start";
+    paramnames[1] = "end";
+    defaults[0] = "20";
+    defaults[1] = "27";
+    add_test(window, "atk_editable_text_set_run_attributes", 2, paramnames, defaults);
+      
+    paramnames[0] = "start";
+    paramnames[1] = "end";
+    defaults[0] = "3";
+    defaults[1] = "5";
+    add_test(window, "atk_editable_text_cut_text", 2, paramnames, defaults);
+
+    paramnames[0] = "position";
+    defaults[0] = "8";
+    add_test(window, "atk_editable_text_paste_text", 1, paramnames, defaults);
+
+    paramnames[0] = "start";
+    paramnames[1] = "end";
+    defaults[0] = "15";
+    defaults[1] = "20";
+    add_test(window, "atk_editable_text_delete_text", 2, paramnames, defaults);
+    paramnames[0] = "start";
+    paramnames[1] = "end";
+    defaults[0] = "5";
+    defaults[1] = "20";
+    add_test(window, "atk_editable_text_copy_text", 2, paramnames, defaults); 
+
+    paramnames[0] = "insert text";
+    paramnames[1] = "position";
+    defaults[0] = "this is my insert";
+    defaults[1] = "15";
+    add_test(window, "atk_editable_text_insert_text", 2, paramnames, defaults);
+  }
+  return window;
+}
+
+/**
+ * add_handlers:
+ * @obj: An #AtkObject
+ *
+ * Sets up text signal handlers.
+ *
+ **/
+void add_handlers(AtkObject *obj)
+{
+  if (!already_accessed_atk_object(obj))
+  {
+    /* Set up signal handlers */
+
+    g_print ("Adding signal handler\n");
+    g_signal_connect_closure_by_id (obj,
+               g_signal_lookup ("text_caret_moved", G_OBJECT_TYPE (obj)),
+               0,
+               g_cclosure_new (G_CALLBACK (_notify_caret_handler),
+                       NULL, NULL),
+               FALSE);
+
+    g_signal_connect_closure (obj, "text_changed::insert",
+               g_cclosure_new (G_CALLBACK (_notify_text_insert_handler),
+                       NULL, NULL),
+               FALSE);
+
+    g_signal_connect_closure (obj, "text_changed::delete",
+               g_cclosure_new (G_CALLBACK (_notify_text_delete_handler),
+                       NULL, NULL),
+               FALSE);
+  }
+}
+
+/**
+ * notify_text_insert_handler:
+ * @obj: A #Gobject
+ * @start_offset: Start offset of insert
+ * @end_offset: End offset of insert.
+ * 
+ * Text inserted singal handler
+ **/
+void
+_notify_text_insert_handler (GObject *obj, int start_offset, int end_offset)
+{
+  g_print ("SIGNAL - Text inserted at position %d, length %d!\n",
+    start_offset, end_offset);
+}
+
+/**
+ * notify_text_delete_handler:
+ * @obj: A #Gobject
+ * @start_offset: Start offset of delete
+ * @end_offset: End offset of delete.
+ * 
+ * Text deleted singal handler
+ **/
+void
+_notify_text_delete_handler (GObject *obj, int start_offset, int end_offset)
+{
+  g_print ("SIGNAL - Text deleted at position %d, length %d!\n",
+    start_offset, end_offset);
+}
+
+/**
+ * notify_caret_handler:
+ * @obj: A #Gobject
+ * @position: Caret position
+ * 
+ * Caret (cursor) moved signal handler.
+ **/
+void
+_notify_caret_handler (GObject *obj, int position)
+{
+  g_print ("SIGNAL - The caret moved to position %d!\n", position);
+}
+
+/**
+ * runtest:
+ * @obj: An #AtkObject
+ * @win_val: The window number
+ *
+ * The callback to run when the "Run Tests" button on the
+ * Test GUI is clicked.
+ **/
+void
+runtest(AtkObject *obj, gint win_val)
+{
+  gint        i, size;
+  gunichar    uni_char;
+  gchar       output[MAX_LINE_SIZE];
+  gchar       **testsOn;
+  testsOn = tests_set(win_val, &size);
+
+  for(i = 0; i < size; i++)
+  {
+    gint      param_int1, param_int2, start, end, j, x, y, height, width;
+    gchar     *param_string1, *param_string2, *param_string3, *text;
+    gboolean  result;
+    gint index;
+
+    if (strcmp(testsOn[i], "atk_text_get_text_at_offset") == 0)
+    {
+      param_string1 = get_arg_of_func(win_val, "atk_text_get_text_at_offset", "offset");  
+      param_int1 = string_to_int(param_string1);
+      
+      _run_offset_test(obj, "at", param_int1, ATK_TEXT_BOUNDARY_WORD_END);
+      _run_offset_test(obj, "at", param_int1, ATK_TEXT_BOUNDARY_WORD_START);
+      _run_offset_test(obj, "at", param_int1, ATK_TEXT_BOUNDARY_LINE_END);
+      _run_offset_test(obj, "at", param_int1, ATK_TEXT_BOUNDARY_LINE_START);
+      _run_offset_test(obj, "at", param_int1, ATK_TEXT_BOUNDARY_SENTENCE_END);
+      _run_offset_test(obj, "at", param_int1, ATK_TEXT_BOUNDARY_SENTENCE_START);
+      _run_offset_test(obj, "at", param_int1, ATK_TEXT_BOUNDARY_CHAR);
+    }
+    
+    if (strcmp(testsOn[i], "atk_text_get_text_after_offset") == 0)
+    {
+      param_string1 = get_arg_of_func(win_val, "atk_text_get_text_after_offset", "offset");  
+      param_int1 = string_to_int(param_string1);
+
+      _run_offset_test(obj, "after", param_int1, ATK_TEXT_BOUNDARY_WORD_END);
+      _run_offset_test(obj, "after", param_int1, ATK_TEXT_BOUNDARY_WORD_START);
+      _run_offset_test(obj, "after", param_int1, ATK_TEXT_BOUNDARY_LINE_END);
+      _run_offset_test(obj, "after", param_int1, ATK_TEXT_BOUNDARY_LINE_START);
+      _run_offset_test(obj, "after", param_int1, ATK_TEXT_BOUNDARY_SENTENCE_END);
+      _run_offset_test(obj, "after", param_int1, ATK_TEXT_BOUNDARY_SENTENCE_START);
+      _run_offset_test(obj, "after", param_int1, ATK_TEXT_BOUNDARY_CHAR);
+    }
+
+    if (strcmp(testsOn[i], "atk_text_get_text_before_offset") == 0)
+    {
+      param_string1 = get_arg_of_func(win_val, "atk_text_get_text_before_offset", "offset");  
+      param_int1 = string_to_int(param_string1);
+      
+      _run_offset_test(obj, "before", param_int1, ATK_TEXT_BOUNDARY_WORD_END);
+      _run_offset_test(obj, "before", param_int1, ATK_TEXT_BOUNDARY_WORD_START);
+      _run_offset_test(obj, "before", param_int1, ATK_TEXT_BOUNDARY_LINE_END);
+      _run_offset_test(obj, "before", param_int1, ATK_TEXT_BOUNDARY_LINE_START);
+      _run_offset_test(obj, "before", param_int1, ATK_TEXT_BOUNDARY_SENTENCE_END);
+      _run_offset_test(obj, "before", param_int1, ATK_TEXT_BOUNDARY_SENTENCE_START);
+      _run_offset_test(obj, "before", param_int1, ATK_TEXT_BOUNDARY_CHAR);
+    }
+    
+    if (strcmp(testsOn[i], "atk_text_get_character_count") == 0)
+    {
+      param_int1 = atk_text_get_character_count (ATK_TEXT (obj));
+      sprintf(output, "\nText character count: %d\n", param_int1);  
+      set_output_buffer(output);
+    }
+    
+    if (strcmp(testsOn[i], "atk_text_get_character_at_offset") == 0)
+    {
+      param_string1 = get_arg_of_func(win_val, "atk_text_get_character_at_offset",
+        "offset"); 
+      uni_char = atk_text_get_character_at_offset (ATK_TEXT(obj),
+        string_to_int(param_string1));
+      sprintf(output, "\nCharacter at offset %d: |%x|\n",
+        string_to_int(param_string1), uni_char);
+      set_output_buffer(output);
+    }
+    if (strcmp(testsOn[i], "atk_text_get_text") == 0)
+    {
+      param_string1 =  get_arg_of_func(win_val, "atk_text_get_text", "position 1");
+      param_string2 = get_arg_of_func(win_val, "atk_text_get_text", "position 2");
+      text = atk_text_get_text (ATK_TEXT (obj), string_to_int(param_string1),
+        string_to_int(param_string2));
+      sprintf(output, "\nText %d, %d: %s\n", string_to_int(param_string1),
+        string_to_int(param_string2), text);
+      g_free (text);  
+      set_output_buffer(output);
+    }
+
+    if (strcmp(testsOn[i], "atk_text_get_caret_offset") == 0)
+    {
+      param_int1 = atk_text_get_caret_offset (ATK_TEXT (obj));
+      if (param_int1 == -1)
+        sprintf(output, "\nCaret offset: |Not Supported|\n");  
+      else
+        sprintf(output, "\nCaret offset: %d\n", param_int1);  
+      set_output_buffer(output);
+    }
+
+    if (strcmp(testsOn[i], "atk_text_set_caret_offset") == 0)
+    {
+      param_string1 = get_arg_of_func(win_val, "atk_text_set_caret_offset", "offset");
+      atk_text_set_caret_offset(ATK_TEXT(obj), string_to_int(param_string1));
+      sprintf(output, "\nPutting caret at offset: |%d|\n",
+        string_to_int(param_string1));
+      param_int1 = atk_text_get_caret_offset (ATK_TEXT (obj));
+
+      if (param_int1 == -1)
+        sprintf(output, "\nCaret offset: |Not Supported|\n");
+      else
+        sprintf(output, "\nCaret offset was set at: |%d|\n", param_int1);
+
+      set_output_buffer(output);
+    }
+    
+    if (strcmp(testsOn[i], "atk_text_get_n_selections") == 0)
+    {
+      param_int1 = atk_text_get_n_selections(ATK_TEXT(obj));
+      if (param_int1 == -1)
+      {
+        sprintf(output, "\nNo selected regions\n");
+        set_output_buffer(output);
+      }
+
+      for (j = 0; j < param_int1; j++)
+      {
+        sprintf(output, "\nNumber of selected text regions is: |%d|\n", j);  
+        set_output_buffer(output);
+
+        text = atk_text_get_selection(ATK_TEXT(obj), j, &start, &end);
+        if (text != NULL)
+        {
+          sprintf(output, "\nSelected text for region %d is: |%s|\n", j, text);
+          set_output_buffer(output);
+          sprintf(output,
+            "\nStart selection bounds: %d\tEnd selection bounds: %d\n",
+            start, end);
+        }
+        else
+        {
+          sprintf(output, "\nNo selected region %d\n", j);
+        }
+
+        set_output_buffer(output);
+      }
+    }
+
+    if (strcmp(testsOn[i], "atk_text_add_selection") == 0)
+    {
+      param_string1 = get_arg_of_func(win_val, "atk_text_add_selection", "start");
+      param_string2 = get_arg_of_func(win_val, "atk_text_add_selection", "end");
+      result = atk_text_add_selection(ATK_TEXT(obj),
+        string_to_int(param_string1), string_to_int(param_string2));   
+      sprintf(output, "\nSet selection bounds between %d, and %d: %s",
+        string_to_int(param_string1), string_to_int(param_string2),
+        result_string[result]);
+      set_output_buffer(output);
+      
+      param_int1 = atk_text_get_n_selections(ATK_TEXT(obj));
+      for (j = 0; j < param_int1; j++)
+      {
+        sprintf(output, "\nNumber of selected text region is: %d\n", j);
+        set_output_buffer(output);
+        text = atk_text_get_selection(ATK_TEXT(obj), j, &start, &end);
+
+        if (text != NULL)
+        {
+          sprintf(output, "\nSelected text for region %d is: |%s|\n", j, text);
+          set_output_buffer(output);
+          sprintf(output,
+            "\nStart selection bounds: %d\tEnd selection bounds: %d\n",
+            start, end);
+        }
+        else
+        {
+          sprintf(output, "\nNo selected region %d\n", j);
+        }
+
+        set_output_buffer(output);
+      }
+    }
+      
+    if (strcmp(testsOn[i], "atk_text_get_selection") == 0)
+    {
+      param_string1 = get_arg_of_func(win_val, "atk_text_get_selection", "selection no");
+      text = atk_text_get_selection(ATK_TEXT(obj),
+        string_to_int(param_string1), &start, &end);
+
+      if (text != NULL)
+      {
+        sprintf(output, "\nSelected text for region %d is: |%s|\n",
+          string_to_int(param_string1), text);
+        set_output_buffer(output);
+        sprintf(output,
+          "\nStart selection bounds: %d\t End selection bounds: %d\n",
+          start, end);
+      }
+      else
+      {
+        sprintf(output, "\nNo selected region %d\n", string_to_int(param_string1));
+      }
+
+      set_output_buffer(output);
+    }
+
+    if (strcmp(testsOn[i], "atk_text_set_selection") == 0)
+    {
+      param_string1 = get_arg_of_func(win_val, "atk_text_set_selection", "selection no");
+      param_string2 = get_arg_of_func(win_val, "atk_text_set_selection", "start");
+      param_string3 = get_arg_of_func(win_val, "atk_text_set_selection", "end");
+      result = atk_text_set_selection(ATK_TEXT(obj), string_to_int(param_string1),
+        string_to_int(param_string2), string_to_int(param_string3));
+      sprintf(output, "Set selection %d's bounds between %d and %d: %s\n",
+        string_to_int(param_string1), string_to_int(param_string2),
+        string_to_int(param_string3), result_string[result]);
+      set_output_buffer(output);
+      text = atk_text_get_selection(ATK_TEXT(obj), string_to_int(param_string1),
+        &start, &end);
+
+      if (text != NULL)
+      {
+        sprintf(output, "Selected text for the reset region %d is: |%s|\n",
+          string_to_int(param_string1), text);
+        set_output_buffer(output);
+        sprintf(output,
+          "\nNew start selection bounds: %d\tNew end selection bounds: %d\n",
+          start, end);
+      }
+      else
+      {
+        sprintf(output, "\nNo selected region %d\n", string_to_int(param_string1));
+      }
+
+      set_output_buffer(output);
+    }
+    
+    if (strcmp(testsOn[i], "atk_text_remove_selection") == 0)
+    {
+      param_string1 = get_arg_of_func(win_val, "atk_text_remove_selection", "selection no");
+      result = atk_text_remove_selection(ATK_TEXT(obj), string_to_int(param_string1));
+      sprintf(output, "Remove selection for region %d: %s\n",
+        string_to_int(param_string1), result_string[result]);
+      set_output_buffer(output);
+      text = atk_text_get_selection(ATK_TEXT(obj),
+        string_to_int(param_string1), &start, &end);
+
+      if (text != NULL)
+        sprintf(output, "\nRemoved regions text should be empty instead of: %s", text);
+      else
+        sprintf(output, "\nRemoved regions text should be empty, this is: ||");
+
+      set_output_buffer(output);
+    }
+
+    if (strcmp(testsOn[i], "atk_text_get_run_attributes") == 0)
+    {
+      gint test_int;
+      param_string1 = get_arg_of_func(win_val, "atk_text_get_run_attributes", "offset");
+      test_int = string_to_int(param_string1);
+      attrib = atk_text_get_run_attributes(ATK_TEXT(obj), test_int, &start, &end);
+      sprintf(output, "get_run_attributes at offset %i:\nStart: %i, End: %i\n", test_int,
+        start, end);
+      set_output_buffer(output);
+      if (attrib != NULL) {
+        GSList *node;
+        index = 0;
+        node = attrib;
+        while (node != NULL)
+        {
+          AtkAttribute* att = node->data;
+
+          sprintf(output, "List index: %i, Name: %s, Value: %s\n", index,
+            att->name, att->value);
+          set_output_buffer(output);
+          node = node->next;
+          index++;
+        }
+        atk_attribute_set_free (attrib);
+      }
+    }
+
+    if (strcmp(testsOn[i], "atk_text_get_default_attributes") == 0)
+    {
+      attrib = atk_text_get_default_attributes(ATK_TEXT(obj));
+      sprintf(output, "get_default_attributes\n");
+      set_output_buffer(output);
+      if (attrib != NULL) {
+        GSList *node;
+        index = 0;
+        node = attrib;
+        while (node != NULL)
+        {
+          AtkAttribute* att = node->data;
+
+          sprintf(output, "List index: %i, Name: %s, Value: %s\n", index,
+            att->name, att->value);
+          set_output_buffer(output);
+          node = node->next;
+          index++;
+        }
+        atk_attribute_set_free (attrib);
+      }
+    }
+    if (strcmp(testsOn[i], "atk_text_get_character_extents") == 0)
+    {
+      gint test_int;
+      param_string1 = get_arg_of_func(win_val, "atk_text_get_character_extents",
+        "offset");
+      param_string2 = get_arg_of_func(win_val, "atk_text_get_character_extents",
+        "coord mode");
+      test_int = string_to_int(param_string1);
+      if (strcmp(param_string2, "ATK_XY_SCREEN") == 0)
+      {
+        atk_text_get_character_extents(ATK_TEXT(obj), test_int, &x, &y, &width,
+          &height, ATK_XY_SCREEN);   
+        sprintf(output,
+          "get_character_extents at offset %i, mode: SCREEN\nX: %i, Y: %i, width: %i, height: %i\n",
+          test_int, x, y, width, height);
+      }
+      else if (strcmp(param_string2, "ATK_XY_WINDOW") == 0)
+      {
+        atk_text_get_character_extents(ATK_TEXT(obj), test_int, &x, &y, &width,
+          &height, ATK_XY_WINDOW);
+        sprintf(output,
+          "get_character_extents at offset %i, mode: WIDGET_WINDOW\nX: %i, Y: %i, width: %i, height: %i\n",
+          test_int, x, y, width, height);
+      }
+      else
+        sprintf(output, "get_character_extents_at_offset: Invalid coord mode argument!");
+           
+      set_output_buffer(output);
+    } 
+
+    if (strcmp(testsOn[i], "atk_text_get_offset_at_point") == 0)
+    {
+      gint test_int;
+      param_string1 = get_arg_of_func(win_val, "atk_text_get_offset_at_point", "x");
+      param_string2 = get_arg_of_func(win_val, "atk_text_get_offset_at_point", "y");
+      param_string3 = get_arg_of_func(win_val, "atk_text_get_offset_at_point", "coord mode");
+      param_int1 = string_to_int(param_string1);
+      param_int2 = string_to_int(param_string2);
+      if (strcmp(param_string3, "ATK_XY_SCREEN") == 0)
+      {
+        test_int = atk_text_get_offset_at_point(ATK_TEXT(obj), param_int1, param_int2,
+          ATK_XY_SCREEN);   
+        if (test_int != -1)
+          sprintf(output, "get_offset_at_point %i,%i mode: SCREEN is %i\n", param_int1, param_int2, test_int);
+        else 
+         sprintf(output, "Cannot get_offset_at_point\n");
+      }
+      else if (strcmp(param_string3, "ATK_XY_WINDOW") == 0)
+      {
+        test_int = atk_text_get_offset_at_point(ATK_TEXT(obj), param_int1, param_int2,
+          ATK_XY_WINDOW);   
+        if (test_int != -1)
+          sprintf(output, "get_offset_at_point %i,%i mode: WIDGET_WINDOW is %i\n", param_int1, param_int2, test_int);
+        else
+         sprintf(output, "Cannot get_offset_at_point\n");
+      }
+      else
+        sprintf(output, "get_offset_at_point: Invalid coord mode argument!");
+           
+      set_output_buffer(output);
+    } 
+    if (ATK_IS_EDITABLE_TEXT(obj))
+    {
+      if (strcmp(testsOn[i], "atk_editable_text_set_run_attributes") == 0)
+      {
+        param_string1 = get_arg_of_func(win_val,
+          "atk_editable_text_set_run_attributes", "start");
+        param_string2 = get_arg_of_func(win_val,
+          "atk_editable_text_set_run_attributes", "end");
+        result = atk_editable_text_set_run_attributes(ATK_EDITABLE_TEXT(obj),
+          attrib, string_to_int(param_string1), string_to_int(param_string2));
+        if (result)
+          sprintf(output, "\nSetting attributes in range %d to %d...OK\n",
+            string_to_int(param_string1), string_to_int(param_string2));
+        else
+          sprintf(output, "\nSetting attributes in range %d to %d...Failed\n",
+            string_to_int(param_string1), string_to_int(param_string2));
+        set_output_buffer(output); 
+      }
+      
+      if (strcmp(testsOn[i], "atk_editable_text_cut_text") == 0)
+      {
+        param_string1 = get_arg_of_func(win_val, "atk_editable_text_cut_text", "start");
+        param_string2 = get_arg_of_func(win_val, "atk_editable_text_cut_text", "end");
+        atk_editable_text_cut_text(ATK_EDITABLE_TEXT(obj),
+          string_to_int(param_string1), string_to_int(param_string2));
+        sprintf(output, "\nCutting text %d to %d...\n",
+          string_to_int(param_string1), string_to_int(param_string2));
+        set_output_buffer(output); 
+      }
+      
+      if (strcmp(testsOn[i], "atk_editable_text_paste_text") == 0)
+      {
+        param_string1 = get_arg_of_func(win_val, "atk_editable_text_paste_text",
+          "position");
+        atk_editable_text_paste_text(ATK_EDITABLE_TEXT(obj),
+          string_to_int(param_string1));
+        sprintf(output, "\nPasting text to %d\n", string_to_int(param_string1));
+        set_output_buffer(output); 
+      }
+      
+      if (strcmp(testsOn[i], "atk_editable_text_delete_text") == 0)
+      {
+        param_string1 = get_arg_of_func(win_val, "atk_editable_text_delete_text", "start");
+        param_string2 = get_arg_of_func(win_val, "atk_editable_text_delete_text", "end");
+        atk_editable_text_delete_text(ATK_EDITABLE_TEXT(obj),
+          string_to_int(param_string1), string_to_int(param_string2));
+        sprintf(output, "\nDeleting text %d to %d...\n",
+          string_to_int(param_string1), string_to_int(param_string2));
+        set_output_buffer(output); 
+      }
+      
+      if (strcmp(testsOn[i], "atk_editable_text_copy_text") == 0)
+      {
+        param_string1 = get_arg_of_func(win_val, "atk_editable_text_copy_text", "start");
+        param_string2 = get_arg_of_func(win_val, "atk_editable_text_copy_text", "end");
+        atk_editable_text_copy_text(ATK_EDITABLE_TEXT(obj),
+          string_to_int(param_string1), string_to_int(param_string2));
+        sprintf(output, "\nCopying text %d to %d...\n",
+          string_to_int(param_string1), string_to_int(param_string2));
+        set_output_buffer(output); 
+      }
+      
+      if (strcmp(testsOn[i], "atk_editable_text_insert_text") == 0)
+      {
+        param_string1 = get_arg_of_func(win_val, "atk_editable_text_insert_text",
+          "insert text");
+        param_string2 = get_arg_of_func(win_val, "atk_editable_text_insert_text",
+          "position");
+        param_int2 = string_to_int(param_string2);
+        atk_editable_text_insert_text(ATK_EDITABLE_TEXT(obj),
+          param_string1, strlen(param_string1), &param_int2);
+        sprintf(output, "\nInserting text at %d...\n", param_int2);
+        set_output_buffer(output); 
+      }
+    }
+  }  
+}
+
+/**
+ * _run_offset_test:
+ * @obj: An #AtkObject
+ * @type: The type of test to run.  Can be "at", "before", or "after".
+ * @offset: The offset into the text buffer.
+ * @boundary: The boundary type.
+ *
+ * Tests the following ATK_TEXT API functions:
+ * atk_text_get_text_at_offset
+ * atk_text_get_text_before_offseet
+ * atk_text_get_text_after_offset
+ **/
+void _run_offset_test(AtkObject * obj, char * type, gint offset,
+  AtkTextBoundary boundary)
+{
+  gchar output[MAX_LINE_SIZE];
+  gchar default_val[5] = "NULL";
+  gchar *text;
+  gint  startOffset, endOffset;
+
+  if (strcmp(type, "at") == 0)
+    text = atk_text_get_text_at_offset (ATK_TEXT (obj),
+       offset, boundary, &startOffset, &endOffset);
+  else if (strcmp(type, "before") == 0)
+    text = atk_text_get_text_before_offset (ATK_TEXT (obj),
+       offset, boundary, &startOffset, &endOffset);
+  else if (strcmp(type, "after") == 0)
+    text = atk_text_get_text_after_offset (ATK_TEXT (obj),
+       offset, boundary, &startOffset, &endOffset);
+  else
+    text = NULL;
+
+  if (text == NULL)
+    text = g_strdup (default_val);
+    
+  if (boundary == ATK_TEXT_BOUNDARY_CHAR)
+    sprintf(output, "\n|%s| Text |%s|  Boundary |BOUNDARY_CHAR|\n",
+      type, text);
+  else if (boundary == ATK_TEXT_BOUNDARY_WORD_START)
+    sprintf(output, "\n|%s| Text |%s| Boundary |BOUNDARY_WORD_START|\n",
+      type, text);
+  else if (boundary == ATK_TEXT_BOUNDARY_WORD_END)
+    sprintf(output, "\n|%s| Text |%s| Boundary |BOUNDARY_WORD_END|\n",
+      type, text);
+  else if (boundary == ATK_TEXT_BOUNDARY_SENTENCE_START)
+    sprintf(output, "\n|%s| Text |%s| Boundary |BOUNDARY_SENTENCE_START|\n",
+      type, text);
+  else if (boundary == ATK_TEXT_BOUNDARY_SENTENCE_END)
+    sprintf(output, "\n|%s| Text |%s| Boundary |BOUNDARY_SENTENCE_END|\n",
+      type, text);
+  else if (boundary == ATK_TEXT_BOUNDARY_LINE_START)
+    sprintf(output, "\n|%s| Text |%s| Boundary |BOUNDARY_LINE_START|\n",
+      type, text);
+  else if (boundary == ATK_TEXT_BOUNDARY_LINE_END)
+    sprintf(output, "\n|%s| Text |%s| Boundary |BOUNDARY_LINE_END|\n",
+      type, text);
+  else
+    sprintf(output, "\n|%s| Text |%s| Boundary |UNKNOWN|\n",
+      type, text);
+
+  g_free (text);  
+  set_output_buffer(output);
+
+  sprintf(output, "Offset %d, startOffset %d, endOffset %d\n",
+    offset, startOffset, endOffset);
+  set_output_buffer(output);
+}
diff --git a/modules/other/gail/tests/testtextlib.h b/modules/other/gail/tests/testtextlib.h
new file mode 100644 (file)
index 0000000..d135c22
--- /dev/null
@@ -0,0 +1,15 @@
+#include <stdio.h>
+#include <gtk/gtk.h>
+#include <atk/atk.h>
+#include "testlib.h"
+
+void add_handlers (AtkObject *obj);
+gint setup_gui (AtkObject *obj, TLruntest test);
+void runtest(AtkObject *obj, gint win_num);
+void _run_offset_test(AtkObject *obj, char * type, gint param_int1, AtkTextBoundary boundary);
+void _notify_caret_handler (GObject *obj, int position);
+void _notify_text_insert_handler (GObject *obj,
+  int start_offset, int end_offset);
+void _notify_text_delete_handler (GObject *obj,
+  int start_offset, int end_offset);                                 
+
diff --git a/modules/other/gail/tests/testtoplevel.c b/modules/other/gail/tests/testtoplevel.c
new file mode 100644 (file)
index 0000000..fa27833
--- /dev/null
@@ -0,0 +1,128 @@
+#include <atk/atk.h>
+#include <atk/atkvalue.h>
+#include <gtk/gtk.h>
+#include "testlib.h"
+
+static void _notify_toplevel_child_added (GObject *obj,
+  guint index, AtkObject *child, gpointer user_data);
+static void _notify_toplevel_child_removed (GObject *obj,
+  guint index, AtkObject *child, gpointer user_data);
+static gboolean _button_press_event_watcher (GSignalInvocationHint *ihint,
+  guint n_param_values, const GValue *param_values, gpointer data);
+
+static guint id;
+static gboolean g_register_listener = FALSE;
+static guint g_signal_listener = 0;
+static gint g_press_count = 0;
+
+static void
+_check_toplevel (AtkObject *obj)
+{
+  AtkObject *root_obj;
+  const gchar *name_string, *version_string;
+  gint max_depth;
+
+  g_print ("Start of _check_toplevel\n");
+  root_obj = atk_get_root();
+
+  if (!already_accessed_atk_object(root_obj))
+    {
+      g_signal_connect_closure (root_obj, "children_changed::add",
+               g_cclosure_new (G_CALLBACK (_notify_toplevel_child_added),
+               NULL, NULL),
+               FALSE);
+
+      g_signal_connect_closure (root_obj, "children_changed::remove",
+               g_cclosure_new (G_CALLBACK (_notify_toplevel_child_removed),
+               NULL, NULL),
+               FALSE);
+    }
+
+  name_string = atk_get_toolkit_name();
+  version_string = atk_get_toolkit_version();
+  g_print ("Toolkit name <%s> version <%s>\n", name_string,
+    version_string);
+
+  if (g_getenv("TEST_ACCESSIBLE_DEPTH") != NULL)
+    max_depth = string_to_int(g_getenv("TEST_ACCESSIBLE_DEPTH"));
+  else
+    max_depth = 2;
+
+  display_children_to_depth(root_obj, max_depth, 0, 0);
+  g_print ("End of _check_toplevel\n");
+
+  if (!g_register_listener)
+    {
+      g_print("Adding global event listener on buttons\n");
+      g_register_listener = TRUE;
+      g_signal_listener = atk_add_global_event_listener(_button_press_event_watcher,
+        "Gtk:GtkButton:pressed");
+    }
+}
+
+static void
+_create_event_watcher (void)
+{
+  id = atk_add_focus_tracker (_check_toplevel);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+  g_print("testtoplevel Module loaded\n");
+
+  _create_event_watcher();
+
+  return 0;
+}
+
+static void _notify_toplevel_child_added (GObject *obj,
+  guint child_index, AtkObject *child, gpointer user_data)
+{
+   g_print ("SIGNAL - Child added - index %d\n", child_index);
+}
+
+static void _notify_toplevel_child_removed (GObject *obj,
+  guint child_index, AtkObject *child, gpointer user_data)
+{
+   g_print ("SIGNAL - Child removed - index %d\n", child_index);
+}
+
+static gboolean
+_button_press_event_watcher (GSignalInvocationHint *ihint,
+                    guint                 n_param_values,
+                    const GValue         *param_values,
+                    gpointer              data)
+{
+  GObject *object;
+  gchar * button_name = (gchar *) data;
+
+  object = g_value_get_object (param_values + 0);
+
+  if (ATK_IS_IMPLEMENTOR(object))
+    {
+      AtkObject * atk_obj =
+        atk_implementor_ref_accessible(ATK_IMPLEMENTOR(object));
+
+      g_print("Button <%s> pressed %d times!\n", button_name,
+        (g_press_count + 1));
+      g_print("Displaying children of Button pressed!\n");
+      display_children(atk_obj, 0, 0);
+
+      if (g_press_count >= 5)
+        {
+          g_print("Removing global event listener on buttons\n");
+          atk_remove_global_event_listener(g_signal_listener);
+          g_signal_listener = 0;
+          g_press_count = 0;
+          g_register_listener = FALSE;
+        }
+      else
+        {
+          g_press_count++;
+        }
+    }
+
+  return TRUE;
+}
+
diff --git a/modules/other/gail/tests/testtreetable.c b/modules/other/gail/tests/testtreetable.c
new file mode 100644 (file)
index 0000000..6ff4925
--- /dev/null
@@ -0,0 +1,499 @@
+#include <stdlib.h>
+#include <testlib.h>
+
+/*
+ * This module is for use with the test program testtreeview
+ */
+static gboolean state_change_watch  (GSignalInvocationHint *ihint,
+                                     guint                  n_param_values,
+                                     const GValue          *param_values,
+                                     gpointer               data);
+
+static void _check_table             (AtkObject         *in_obj);
+static void _check_cell_actions      (AtkObject         *in_obj);
+
+static gint _find_expander_column    (AtkTable          *table);
+static void _check_expanders         (AtkTable          *table,
+                                      gint              expander_column);
+static void _runtest                 (AtkObject         *obj);
+static void _create_event_watcher    (void);
+static void row_inserted             (AtkObject         *obj,
+                                      gint              row,
+                                      gint              count);
+static void row_deleted              (AtkObject         *obj,
+                                      gint              row,
+                                      gint              count);
+static AtkObject *table_obj = NULL;
+static gint expander_column = -1;
+static gboolean editing_cell = FALSE;
+
+static gboolean 
+state_change_watch (GSignalInvocationHint *ihint,
+                    guint                  n_param_values,
+                    const GValue          *param_values,
+                    gpointer               data)
+{
+  GObject *object;
+  gboolean state_set;
+  G_CONST_RETURN gchar *state_name;
+  AtkStateType state_type;
+
+  object = g_value_get_object (param_values + 0);
+  g_return_val_if_fail (ATK_IS_OBJECT (object), FALSE);
+
+  state_name = g_value_get_string (param_values + 1);
+  state_set = g_value_get_boolean (param_values + 2);
+
+  g_print ("Object type: %s state: %s set: %d\n", 
+           g_type_name (G_OBJECT_TYPE (object)), state_name, state_set);
+  state_type = atk_state_type_for_name (state_name);
+  if (state_type == ATK_STATE_EXPANDED)
+    {
+      AtkObject *parent = atk_object_get_parent (ATK_OBJECT (object));
+
+      if (ATK_IS_TABLE (parent))
+        _check_expanders (ATK_TABLE (parent), expander_column);
+    }
+  return TRUE;
+}
+
+static void 
+_check_table (AtkObject *in_obj)
+{
+  AtkObject *obj;
+  AtkRole role[2];
+  AtkRole obj_role;
+  static gboolean emission_hook_added = FALSE;
+
+  if (!emission_hook_added)
+    {
+      emission_hook_added = TRUE;
+      g_signal_add_emission_hook (
+                    g_signal_lookup ("state-change", ATK_TYPE_OBJECT),
+      /*
+       * To specify an emission hook for a particular state, e.g. 
+       * ATK_STATE_EXPANDED, instead of 0 use
+       * g_quark_from_string (atk_state_type_get_name (ATK_STATE_EXPANDED))
+       */  
+                    0,
+                    state_change_watch, NULL, (GDestroyNotify) NULL);
+    }
+
+  role[0] = ATK_ROLE_TABLE;
+  role[1] = ATK_ROLE_TREE_TABLE;
+  
+  g_print ("focus event for: %s\n", g_type_name (G_OBJECT_TYPE (in_obj)));
+
+  _check_cell_actions (in_obj);
+
+  obj_role = atk_object_get_role (in_obj);
+  if (obj_role == ATK_ROLE_TABLE_CELL)
+    obj = atk_object_get_parent (in_obj);
+  else
+    obj = find_object_by_role (in_obj, role, 2);
+  if (obj == NULL)
+    return;
+
+  editing_cell = FALSE;
+
+  if (obj != table_obj)
+    {
+      table_obj = obj;
+      g_print("Found %s table\n", g_type_name (G_OBJECT_TYPE (obj)));
+      g_signal_connect (G_OBJECT (obj),
+                        "row_inserted",
+                        G_CALLBACK (row_inserted),
+                        NULL);
+      g_signal_connect (G_OBJECT (obj),
+                        "row_deleted",
+                        G_CALLBACK (row_deleted),
+                        NULL);
+      /*
+       * Find expander column
+       */
+      if (ATK_IS_TABLE (obj))
+        {
+          if (atk_object_get_role (obj) == ATK_ROLE_TREE_TABLE)
+            {
+              expander_column = _find_expander_column (ATK_TABLE (obj));
+              if (expander_column == -1)
+                {
+                  g_warning ("Expander column not found\n");
+                  return;
+                }
+            }
+          _runtest(obj);
+        }
+      else
+        g_warning ("Table does not support AtkTable interface\n");
+    }
+  else 
+    {
+      /*
+       * We do not call these functions at the same time as we set the 
+       * signals as the GtkTreeView may not be displayed yet.
+       */
+      gint x, y, width, height, first_x, last_x, last_y;
+      gint first_row, last_row, first_column, last_column;
+      gint index;
+      AtkObject *first_child, *last_child, *header;
+      AtkComponent *component = ATK_COMPONENT (obj);
+      AtkTable *table = ATK_TABLE (obj);
+
+      atk_component_get_extents (component, 
+                                 &x, &y, &width, &height, ATK_XY_WINDOW);
+      g_print ("WINDOW: x: %d y: %d width: %d height: %d\n",
+               x, y, width, height);
+      atk_component_get_extents (component, 
+                                 &x, &y, &width, &height, ATK_XY_SCREEN);
+      g_print ("SCREEN: x: %d y: %d width: %d height: %d\n",
+               x, y, width, height);
+      last_x = x + width - 1;
+      last_y = y + height - 1;
+      first_x = x; 
+      header = atk_table_get_column_header (table, 0);
+      if (header)
+        {
+          atk_component_get_extents (ATK_COMPONENT (header), 
+                                     &x, &y, &width, &height, ATK_XY_SCREEN);
+          g_print ("Got column header: x: %d y: %d width: %d height: %d\n",
+                   x, y, width, height);
+          y += height;
+        }
+      first_child = atk_component_ref_accessible_at_point (component, 
+                                             first_x, y, ATK_XY_SCREEN);
+      atk_component_get_extents (ATK_COMPONENT (first_child), 
+                                 &x, &y, &width, &height, ATK_XY_SCREEN);
+      g_print ("first_child: x: %d y: %d width: %d height: %d\n",
+               x, y, width, height);
+      index = atk_object_get_index_in_parent (ATK_OBJECT (first_child));
+      first_row = atk_table_get_row_at_index (table, index);
+      first_column = atk_table_get_column_at_index (table, index);
+      g_print ("first_row: %d first_column: %d index: %d\n",
+               first_row, first_column, index);
+      g_assert (index == atk_table_get_index_at (table, first_row, first_column));
+      last_child = atk_component_ref_accessible_at_point (component, 
+                                     last_x, last_y, ATK_XY_SCREEN);
+      if (last_child == NULL)
+        {
+          /* The TreeView may be bigger than the data */
+          gint n_children;
+
+          n_children = atk_object_get_n_accessible_children (obj);
+          last_child = atk_object_ref_accessible_child (obj, n_children - 1);
+        }
+      atk_component_get_extents (ATK_COMPONENT (last_child), 
+                                 &x, &y, &width, &height, ATK_XY_SCREEN);
+      g_print ("last_child: x: %d y: %d width: %d height: %d\n",
+               x, y, width, height);
+      index = atk_object_get_index_in_parent (ATK_OBJECT (last_child));
+      last_row = atk_table_get_row_at_index (table, index);
+      last_column = atk_table_get_column_at_index (table, index);
+      g_print ("last_row: %d last_column: %d index: %d\n",
+               last_row, last_column, index);
+      g_assert (index == atk_table_get_index_at (table, last_row, last_column));
+      g_object_unref (first_child);
+      g_object_unref (last_child);
+
+      if (expander_column >= 0)
+        {
+          gint n_rows, i;
+          gint x, y, width, height;
+
+          n_rows = atk_table_get_n_rows (table);
+          for (i = 0; i < n_rows; i++)
+            {
+              AtkObject *child_obj;
+              AtkStateSet *state_set;
+              gboolean showing;
+
+              child_obj = atk_table_ref_at (table, i, expander_column);
+              state_set = atk_object_ref_state_set (child_obj);
+              showing =  atk_state_set_contains_state (state_set, 
+                                                       ATK_STATE_SHOWING);
+              g_object_unref (state_set);
+              atk_component_get_extents (ATK_COMPONENT (child_obj), 
+                                         &x, &y, &width, &height, 
+                                         ATK_XY_SCREEN);
+              g_object_unref (child_obj);
+              if (showing)
+                g_print ("Row: %d Column: %d x: %d y: %d width: %d height: %d\n", 
+                         i, expander_column, x, y, width, height);
+            }
+        }
+    }
+}
+
+static void 
+_check_cell_actions (AtkObject *in_obj)
+{
+  AtkRole role;
+  AtkAction *action;
+  gint n_actions, i;
+
+  role = atk_object_get_role (in_obj);
+  if (role != ATK_ROLE_TABLE_CELL)
+    return;
+
+  if (!ATK_IS_ACTION (in_obj))
+    return;
+
+  if (editing_cell)
+    return;
+
+  action = ATK_ACTION (in_obj);
+
+  n_actions = atk_action_get_n_actions (action);
+
+  for (i = 0; i < n_actions; i++)
+    {
+      G_CONST_RETURN gchar* name;
+
+      name = atk_action_get_name (action, i);
+      g_print ("Action %d is %s\n", i, name);
+#if 0
+      if (strcmp (name, "edit") == 0)
+        {
+          editing_cell = TRUE;
+          atk_action_do_action (action, i);
+        }
+#endif
+    }
+  return;
+}
+
+static gint 
+_find_expander_column (AtkTable *table)
+{
+  gint n_columns, i;
+  gint retval = -1;
+
+  n_columns = atk_table_get_n_columns (table);
+  for (i = 0; i < n_columns; i++)
+    {
+      AtkObject *cell;
+      AtkRelationSet *relation_set;
+
+      cell = atk_table_ref_at (table, 0, i);
+      relation_set =  atk_object_ref_relation_set (cell);
+      if (atk_relation_set_contains (relation_set, 
+                                     ATK_RELATION_NODE_CHILD_OF))
+        retval = i;
+      g_object_unref (relation_set);
+      g_object_unref (cell);
+      if (retval >= 0)
+        break;
+    }
+  return retval;
+}
+
+static void
+_check_expanders (AtkTable *table,
+                  gint     expander_column)
+{
+  gint n_rows, i;
+
+  n_rows = atk_table_get_n_rows (table);
+
+  for (i = 0; i < n_rows; i++)
+    {
+      AtkObject *cell;
+      AtkRelationSet *relation_set;
+      AtkRelation *relation;
+      GPtrArray *target;
+      gint j;
+
+      cell = atk_table_ref_at (table, i, expander_column);
+
+      relation_set =  atk_object_ref_relation_set (cell);
+      relation = atk_relation_set_get_relation_by_type (relation_set, 
+                                     ATK_RELATION_NODE_CHILD_OF);
+      g_assert (relation);
+      target = atk_relation_get_target (relation);
+      g_assert (target->len == 1);
+      for (j = 0; j < target->len; j++)
+        {
+          AtkObject *target_obj;
+          AtkRole role;
+          gint target_index, target_row;
+
+          target_obj = g_ptr_array_index (target, j);
+          role = atk_object_get_role (target_obj);
+
+          switch (role)
+            {
+            case ATK_ROLE_TREE_TABLE:
+              g_print ("Row %d is top level\n", i);
+              break;
+            case ATK_ROLE_TABLE_CELL:
+              target_index = atk_object_get_index_in_parent (target_obj);
+              target_row = atk_table_get_row_at_index (table, target_index);
+              g_print ("Row %d has parent at %d\n", i, target_row);
+              break;
+            default:
+              g_assert_not_reached ();
+            } 
+        }
+      g_object_unref (relation_set);
+      g_object_unref (cell);
+    }
+}
+
+static void
+_create_event_watcher (void)
+{
+  atk_add_focus_tracker (_check_table);
+}
+
+int
+gtk_module_init (gint argc, 
+                 char *argv[])
+{
+  g_print ("testtreetable Module loaded\n");
+
+  _create_event_watcher ();
+
+  return 0;
+}
+
+static void
+_runtest (AtkObject *obj)
+{
+  AtkObject *child_obj;
+  AtkTable *table;
+  AtkObject *caption;
+  gint i;
+  gint n_cols, n_rows, n_children; 
+
+  table = ATK_TABLE (obj);
+  n_children = atk_object_get_n_accessible_children (ATK_OBJECT (obj));
+  n_cols = atk_table_get_n_columns (table);
+  n_rows = atk_table_get_n_rows (table);
+  g_print ("n_children: %d n_rows: %d n_cols: %d\n", 
+            n_children, n_rows, n_cols);
+  
+  for (i = 0; i < n_rows; i++)
+    {
+      gint index = atk_table_get_index_at (table, i, expander_column);
+      gint index_in_parent;
+
+      child_obj = atk_table_ref_at (table, i, expander_column);
+      index_in_parent = atk_object_get_index_in_parent (child_obj);
+      g_print ("index: %d %d row %d column %d\n", index, index_in_parent, i, expander_column);
+      
+      g_object_unref (child_obj);
+    }
+  caption = atk_table_get_caption (table);
+  if (caption)
+    {
+      const gchar *caption_name = atk_object_get_name (caption);
+
+      g_print ("Caption: %s\n", caption_name ? caption_name : "<null>");
+    }
+  for (i = 0; i < n_cols; i++)
+    {
+      AtkObject *header;
+
+      header = atk_table_get_column_header (table, i);
+      g_print ("Header for column %d is %p\n", i, header);
+      if (header)
+        {
+         const gchar *name;
+         AtkRole role;
+          AtkObject *parent;
+          AtkObject *child;
+          gint index;
+
+          name = atk_object_get_name (header);
+          role = atk_object_get_role (header);
+          parent = atk_object_get_parent (header);
+
+          if (parent)
+            {
+              index = atk_object_get_index_in_parent (header);
+              g_print ("Parent: %s index: %d\n", G_OBJECT_TYPE_NAME (parent), index);
+              child = atk_object_ref_accessible_child (parent, 0);
+              g_print ("Child: %s %p\n", G_OBJECT_TYPE_NAME (child), child);
+              if (index >= 0)
+                {
+                  child = atk_object_ref_accessible_child (parent, index);
+                  g_print ("Index: %d child: %s\n", index, G_OBJECT_TYPE_NAME (child));
+                  g_object_unref (child);
+                }
+            }
+          else
+            g_print ("Parent of header is NULL\n");
+          g_print ("%s %s %s\n", G_OBJECT_TYPE_NAME (header), name ? name: "<null>", atk_role_get_name (role));
+        }
+    }
+}
+
+static void 
+row_inserted (AtkObject *obj,
+              gint      row,
+              gint      count)
+{
+#if 0
+  GtkWidget *widget;
+  GtkTreeView *tree_view;
+  GtkTreeModel *tree_model;
+#endif
+  gint index;
+
+  g_print ("row_inserted: row: %d count: %d\n", row, count);
+  index = atk_table_get_index_at (ATK_TABLE (obj), row+1, 0);
+  g_print ("index for first column of next row is %d\n", index);
+
+#if 0
+  widget = GTK_ACCESSIBLE (obj)->widget;
+  tree_view = GTK_TREE_VIEW (widget);
+  tree_model = gtk_tree_view_get_model (tree_view);
+  if (GTK_IS_TREE_STORE (tree_model))
+    {
+      GtkTreeStore *tree_store;
+      GtkTreePath *tree_path;
+      GtkTreeIter tree_iter;
+
+      tree_store = GTK_TREE_STORE (tree_model);
+      tree_path = gtk_tree_path_new_from_string ("3:0");
+      gtk_tree_model_get_iter (tree_model, &tree_iter, tree_path);
+      gtk_tree_path_free (tree_path);
+      gtk_tree_store_remove (tree_store, &tree_iter);
+    }
+#endif
+}
+
+static void 
+row_deleted (AtkObject *obj,
+             gint      row,
+             gint      count)
+{
+#if 0
+  GtkWidget *widget;
+  GtkTreeView *tree_view;
+  GtkTreeModel *tree_model;
+#endif
+  gint index;
+
+  g_print ("row_deleted: row: %d count: %d\n", row, count);
+  index = atk_table_get_index_at (ATK_TABLE (obj), row+1, 0);
+  g_print ("index for first column of next row is %d\n", index);
+
+#if 0
+  widget = GTK_ACCESSIBLE (obj)->widget;
+  tree_view = GTK_TREE_VIEW (widget);
+  tree_model = gtk_tree_view_get_model (tree_view);
+  if (GTK_IS_TREE_STORE (tree_model))
+    {
+      GtkTreeStore *tree_store;
+      GtkTreePath *tree_path;
+      GtkTreeIter tree_iter, new_iter;
+
+      tree_store = GTK_TREE_STORE (tree_model);
+      tree_path = gtk_tree_path_new_from_string ("2");
+      gtk_tree_model_get_iter (tree_model, &tree_iter, tree_path);
+      gtk_tree_path_free (tree_path);
+      gtk_tree_store_insert_before (tree_store, &new_iter, NULL, &tree_iter);
+    }
+#endif
+}
diff --git a/modules/other/gail/tests/testvalues.c b/modules/other/gail/tests/testvalues.c
new file mode 100644 (file)
index 0000000..fcb8119
--- /dev/null
@@ -0,0 +1,194 @@
+#include <string.h>
+#include <atk/atk.h>
+#include <atk/atkvalue.h>
+#include <gtk/gtk.h>
+
+static void _traverse_children (AtkObject *obj);
+static void _add_handler (AtkObject *obj);
+static void _check_values (AtkObject *obj);
+static void _value_change_handler (AtkObject   *obj,
+                                      AtkPropertyValues *values);
+
+static guint id;
+
+static void _value_change_handler (AtkObject   *obj,
+                                   AtkPropertyValues   *values)
+{
+  G_CONST_RETURN gchar *type_name = g_type_name (G_TYPE_FROM_INSTANCE (obj));
+   GValue *value_back, val;
+
+  value_back = &val;
+    
+  if (!ATK_IS_VALUE (obj)) { 
+       return;
+  }
+
+  if (strcmp (values->property_name, "accessible-value") == 0) {
+       g_print ("_value_change_handler: Accessible Type: %s\n",
+           type_name ? type_name : "NULL");
+       if(G_VALUE_HOLDS_DOUBLE (&values->new_value))
+    {
+               g_print( "adjustment value changed : new value: %f\n", 
+               g_value_get_double (&values->new_value));
+       }
+
+       g_print("Now calling the AtkValue interface functions\n");
+
+       atk_value_get_current_value (ATK_VALUE(obj), value_back);
+       g_return_if_fail (G_VALUE_HOLDS_DOUBLE (value_back));
+       g_print ("atk_value_get_current_value returns %f\n",
+                       g_value_get_double (value_back) );
+
+       atk_value_get_maximum_value (ATK_VALUE (obj), value_back);
+       g_return_if_fail (G_VALUE_HOLDS_DOUBLE (value_back));
+       g_print ("atk_value_get_maximum returns %f\n",
+                       g_value_get_double (value_back));
+
+       atk_value_get_minimum_value (ATK_VALUE (obj), value_back);
+       g_return_if_fail (G_VALUE_HOLDS_DOUBLE (value_back));
+       g_print ("atk_value_get_minimum returns %f\n", 
+                       g_value_get_double (value_back));
+       
+    }
+  
+}
+
+static void _traverse_children (AtkObject *obj)
+{
+  gint n_children, i;
+
+  n_children = atk_object_get_n_accessible_children (obj);
+  for (i = 0; i < n_children; i++)
+  {
+    AtkObject *child;
+
+    child = atk_object_ref_accessible_child (obj, i);
+    _add_handler (child);
+    _traverse_children (child);
+    g_object_unref (G_OBJECT (child));
+  }
+}
+
+static void _add_handler (AtkObject *obj)
+{
+  static GPtrArray *obj_array = NULL;
+  gboolean found = FALSE;
+  gint i;
+
+  /*
+   * We create a property handler for each object if one was not associated 
+   * with it already.
+   *
+   * We add it to our array of objects which have property handlers; if an
+   * object is destroyed it remains in the array.
+   */
+  if (obj_array == NULL)
+    obj_array = g_ptr_array_new ();
+  for (i = 0; i < obj_array->len; i++)
+  {
+    if (obj == g_ptr_array_index (obj_array, i))
+    {
+      found = TRUE;
+      break;
+    }
+  }
+  if (!found)
+  {
+    atk_object_connect_property_change_handler (obj,
+                   (AtkPropertyChangeHandler*) _value_change_handler);
+    g_ptr_array_add (obj_array, obj);
+  }
+}
+
+static void _set_values (AtkObject *obj) {
+
+  GValue *value_back, val;
+  static gint count = 0;
+  gdouble double_value;
+
+  value_back = &val;
+
+  if(ATK_IS_VALUE(obj)) {
+       /* Spin button also inherits the text interfaces from GailEntry. 
+        * Check when spin button recieves focus.
+     */
+
+       if(ATK_IS_TEXT(obj) && ATK_IS_EDITABLE_TEXT(obj)) {
+               if(count == 0) {        
+                       gint x;
+                       gchar* text;
+                       count++;
+                       x = atk_text_get_character_count (ATK_TEXT (obj));
+                       text = atk_text_get_text (ATK_TEXT (obj), 0, x);
+                       g_print("Text : %s\n", text);
+                       text = "5.7";
+                       atk_editable_text_set_text_contents(ATK_EDITABLE_TEXT(obj),text);
+                       g_print("Set text to %s\n",text);
+                       atk_value_get_current_value(ATK_VALUE(obj), value_back);
+                       g_return_if_fail (G_VALUE_HOLDS_DOUBLE (value_back));
+                       g_print("atk_value_get_current_value returns %f\n", 
+                               g_value_get_double( value_back));
+                       } 
+       } else {
+               memset (value_back, 0, sizeof (GValue));
+               g_value_init (value_back, G_TYPE_DOUBLE);
+               g_value_set_double (value_back, 10.0);  
+               if (atk_value_set_current_value (ATK_VALUE (obj), value_back))
+               {
+                       double_value = g_value_get_double (value_back);
+                       g_print("atk_value_set_current_value returns %f\n", 
+                       double_value);
+               }
+       }
+  }
+}
+
+static void _check_values (AtkObject *obj)
+{
+  static gint calls = 0;
+  AtkRole role;
+
+  g_print ("Start of _check_values\n");
+
+  _set_values(obj);
+
+  _add_handler (obj);
+
+  if (++calls < 2)
+  { 
+    /*
+     * Just do this on this on the first 2 objects visited
+     */
+    atk_object_set_name (obj, "test123");
+    atk_object_set_description (obj, "test123");
+  }
+
+  role = atk_object_get_role (obj);
+
+  if (role == ATK_ROLE_FRAME || role == ATK_ROLE_DIALOG)
+  {
+    /*
+     * Add handlers to all children.
+     */
+    _traverse_children (obj);
+  }
+  g_print ("End of _check_values\n");
+}
+
+static void
+_create_event_watcher (void)
+{
+  id = atk_add_focus_tracker (_check_values);
+}
+
+int
+gtk_module_init(gint argc, char* argv[])
+{
+  g_print("testvalues Module loaded\n");
+
+  _create_event_watcher();
+
+  return 0;
+}